0
0
mirror of https://github.com/netwide-assembler/nasm.git synced 2025-09-22 10:43:39 -04:00
Files
nasm/asm/assemble.c
H. Peter Anvin a0396faf3b Fix control/debug register patterns
The control and debug registers are always using the default operand
size. It is probably easiest to just encode it explicitly for now.

Control registers are particularly weird because of the AMD "lock as
REX.R" hack...

Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
2025-09-19 16:26:35 -07:00

4355 lines
130 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.
*
* ----------------------------------------------------------------------- */
/*
* assemble.c code generation for the Netwide Assembler
*
* The entry points to this module are insn_size() for the non-final
* passes, and assemble() for the final pass.
*/
#include "compiler.h"
#include "nasm.h"
#include "nasmlib.h"
#include "error.h"
#include "assemble.h"
#include "insns.h"
#include "tables.h"
#include "disp8.h"
#include "listing.h"
#include "dbginfo.h"
enum match_result {
/*
* Matching errors. These should be sorted so that more specific
* errors (higher priority) come later in the sequence.
*/
MERR_INVALOP,
MERR_OPSIZEINVAL,
MERR_OPSIZEMISSING,
MERR_OPSIZEMISMATCH,
MERR_BRNOTHERE,
MERR_BRNUMMISMATCH,
MERR_MASKNOTHERE,
MERR_DECONOTHERE,
MERR_BADZU,
MERR_MEMZU,
MERR_BADNF,
MERR_REQNF,
MERR_BADCPU,
MERR_BADMODE,
MERR_BADHLE,
MERR_ENCMISMATCH,
MERR_BADBND,
MERR_BADREPNE,
MERR_REGSETSIZE,
MERR_REGSET,
MERR_WRONGIMM,
/*
* Matching successes in decreasing order of fuzziness;
* the fuzziness factor amounts to the factors (normally
* operands) fuzzed.
*/
MOK_FIRST, /* First successful anything */
MOK_FUZZY8,
MOK_FUZZY7, /* Matching unconditionally OK */
MOK_FUZZY6,
MOK_FUZZY5,
MOK_FUZZY4,
MOK_FUZZY3,
MOK_FUZZY2,
MOK_FUZZY1,
MOK_GOOD
};
#define GEN_SIB(scale, index, base) \
(((scale) << 6) | ((index) << 3) | ((base)))
#define GEN_MODRM(mod, reg, rm) \
(((mod) << 6) | (((reg) & 7) << 3) | ((rm) & 7))
static int64_t assemble(insn *instruction);
static int64_t insn_size(insn *instruction);
static int64_t calcsize(insn *, const struct itemplate *);
static int emit_prefixes(struct out_data *data, const insn *ins);
static void gencode(struct out_data *data, insn *ins);
static enum match_result find_match(const struct itemplate **tempp,
insn *instruction);
static enum match_result matches(const struct itemplate *, const insn *);
static opflags_t regflag(const operand *);
static int32_t regval(const operand *);
static uint32_t rexflags(int, opflags_t, uint32_t);
static uint32_t op_rexflags(const operand *, uint32_t);
static uint32_t op_evexflags(const operand *, uint32_t);
static void insn_early_setup(insn *);
static int process_ea(operand *input, int rfield, opflags_t rflags,
insn *ins, enum ea_type expected,
const char **errmsgp);
/* Convert a prefix to a byte value */
static int prefix_byte(enum prefixes pfx, const int bits);
/*
* Convert operand/address/mode size to a BITS opflag constant.
* This is not valid for 80+ bits!
*/
static inline opflags_t mode_to_op(unsigned int bits)
{
nasm_static_assert((BITS8 >> SIZE_SHIFT) == 1);
nasm_static_assert(BITS16 == (BITS8 << 1));
nasm_static_assert(BITS32 == (BITS8 << 2));
nasm_static_assert(BITS64 == (BITS8 << 3));
return (opflags_t)bits << (SIZE_SHIFT - 3);
}
/*
* Return any of REX_[BXR]1 corresponding to non-GPR registers by
* masking them with the REX_[BXR]V flags.
*/
static inline const_func uint32_t rex_highvec(uint32_t rexflags)
{
return rexflags & (rexflags >> 4) & REX_BXR1;
}
/* Get the pointer to an operand if it exits */
static inline struct operand *get_operand(insn *ins, unsigned int n)
{
if (n >= (unsigned int)ins->operands)
return NULL;
else
return &ins->oprs[n];
}
static inline const struct operand *
get_operand_const(const insn *ins, unsigned int n)
{
if (n >= (unsigned int)ins->operands)
return NULL;
else
return &ins->oprs[n];
}
static inline bool absolute_op(const struct operand *o)
{
return o && o->segment == NO_SEG && o->wrt == NO_SEG &&
!(o->opflags & OPFLAG_RELATIVE);
}
static int has_prefix(const insn * ins, enum prefix_pos pos, int prefix)
{
return ins->prefixes[pos] == prefix;
}
static int assert_no_prefix(insn * ins, enum prefix_pos pos)
{
if (ins->prefixes[pos]) {
nasm_nonfatal("invalid %s prefix", prefix_name(ins->prefixes[pos]));
return -1;
}
return 0;
}
static const char *size_name(int size)
{
switch (size) {
case 1:
return "byte";
case 2:
return "word";
case 4:
return "dword";
case 8:
return "qword";
case 10:
return "tword";
case 16:
return "oword";
case 32:
return "yword";
case 64:
return "zword";
default:
return "???";
}
}
static void warn_overflow(int size, const char *prefix, const char *suffix)
{
const char *pfxsp, *sufsp;
pfxsp = sufsp = " ";
if (!prefix)
prefix = ++pfxsp;
if (!suffix)
suffix = ++sufsp;
nasm_warn(WARN_NUMBER_OVERFLOW|ERR_PASS2,
"%s%s%s%s%s exceeds bounds",
prefix, pfxsp, size_name(size), sufsp, suffix);
}
static void warn_overflow_const(int64_t data, int size)
{
if (overflow_general(data, size))
warn_overflow(size, NULL, NULL);
}
static void warn_overflow_out(int64_t data, int size,
enum out_flags flags, const char *what)
{
bool err;
const char *prefix;
if (flags & OUT_NOWARN)
return;
if (flags & OUT_SIGNED) {
prefix = "signed";
err = overflow_signed(data, size);
} else if (flags & OUT_UNSIGNED) {
prefix = "unsigned";
err = overflow_unsigned(data, size);
} else {
prefix = NULL;
err = overflow_general(data, size);
}
if (err)
warn_overflow(size, prefix, what);
}
/*
* Collect macro-related debug information, if applicable.
*/
static void debug_macro_out(const struct out_data *data)
{
struct debug_macro_addr *addr;
uint64_t start = data->loc.offset;
uint64_t end = start + data->size;
addr = debug_macro_get_addr(data->loc.segment);
while (addr) {
if (!addr->len)
addr->start = start;
addr->len = end - addr->start;
addr = addr->up;
}
}
/*
* This routine wrappers the real output format's output routine,
* in order to pass a copy of the data off to the listing file
* generator at the same time, flatten unnecessary relocations,
* and verify backend compatibility.
*/
/*
* This warning is currently issued by backends, but in the future
* this code should be centralized.
*
*!zeroing [on] \c{RES}\e{x} in initialized section becomes zero
*! a \c{RES}\e{x} directive was used in a section which contains
*! initialized data, and the output format does not support
*! this. Instead, this will be replaced with explicit zero
*! content, which may produce a large output file.
*/
/*
* Add the entries in struct out_data for the rather bizarre legacy
* backend interface, and then submit to the backend.
*
* The "data" parameter for the output function points to a "int64_t",
* containing the address of the target in question, unless the type is
* OUT_RAWDATA, in which case it points to an "uint8_t"
* array.
*
* Exceptions are OUT_RELxADR, which denote an x-byte relocation
* which will be a relative jump. For this we need to know the
* distance in bytes from the start of the relocated record until
* the end of the containing instruction. _This_ is what is stored
* in the size part of the parameter, in this case.
*
* Also OUT_RESERVE denotes reservation of N bytes of BSS space,
* and the contents of the "data" parameter is irrelevant.
*/
static void nasm_ofmt_output(struct out_data *data)
{
const void *dptr = data->data;
enum out_type type = data->type;
int32_t tsegment = data->tsegment;
int32_t twrt = data->twrt;
uint64_t size = data->size;
switch (data->type) {
case OUT_RELADDR:
switch (data->size) {
case 1:
type = OUT_REL1ADR;
break;
case 2:
type = OUT_REL2ADR;
break;
case 4:
type = OUT_REL4ADR;
break;
case 8:
type = OUT_REL8ADR;
break;
default:
panic();
break;
}
dptr = &data->toffset;
size = data->relbase - data->loc.offset;
break;
case OUT_SEGMENT:
type = OUT_ADDRESS;
if (tsegment != NO_SEG && tsegment < SEG_ABS)
tsegment |= 1;
/* fall through */
case OUT_ADDRESS:
dptr = &data->toffset;
size = (data->flags & OUT_SIGNED) ? -data->size : data->size;
break;
case OUT_RAWDATA:
case OUT_RESERVE:
tsegment = twrt = NO_SEG;
break;
case OUT_ZERODATA:
tsegment = twrt = NO_SEG;
type = OUT_RAWDATA;
dptr = zero_buffer;
break;
default:
panic();
break;
}
data->legacy.data = dptr;
data->legacy.type = type;
data->legacy.size = size;
data->legacy.tsegment = tsegment;
data->legacy.twrt = twrt;
ofmt->output(data);
}
static void out(struct out_data *data)
{
static struct last_debug_info {
struct src_location where;
int32_t segment;
} dbg;
union {
uint8_t b[8];
uint64_t q;
} xdata;
size_t asize, amax;
uint64_t zeropad = 0;
uint64_t real_size;
int64_t addrval;
int32_t fixseg; /* Segment for which to produce fixed data */
if (!data->size)
return; /* Nothing to do */
real_size = data->size;
/*
* Convert addresses to RAWDATA if possible
* XXX: not all backends want this for global symbols!!!!
*/
switch (data->type) {
case OUT_ADDRESS:
addrval = data->toffset;
fixseg = NO_SEG; /* Absolute address is fixed data */
goto address;
case OUT_RELADDR:
addrval = data->toffset - data->relbase;
fixseg = data->loc.segment; /* Our own segment is fixed data */
goto address;
address:
nasm_assert(data->size <= 8);
asize = data->size;
amax = ofmt->maxbits >> 3; /* Maximum address size in bytes */
if (data->tsegment == fixseg && data->twrt == NO_SEG) {
if (!(ofmt->flags & OFMT_KEEP_ADDR)) {
if (asize >= (size_t)(data->bits >> 3)) {
/* Support address space wrapping for low-bit modes */
data->flags &= ~OUT_SIGNMASK;
}
warn_overflow_out(addrval, asize, data->flags, data->what);
xdata.q = htole64(addrval);
data->data = xdata.b;
data->type = OUT_RAWDATA;
asize = amax = 0; /* No longer an address */
}
} else {
/*!
*!reloc-abs-byte [off] 8-bit absolute section-crossing relocation
*! warns that an 8-bit absolute relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
/*!
*!reloc-abs-word [off] 16-bit absolute section-crossing relocation
*! warns that a 16-bit absolute relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
/*!
*!reloc-abs-dword [off] 32-bit absolute section-crossing relocation
*! warns that a 32-bit absolute relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
/*!
*!reloc-abs-qword [off] 64-bit absolute section-crossing relocation
*! warns that a 64-bit absolute relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
/*!
*!reloc-rel-byte [off] 8-bit relative section-crossing relocation
*! warns that an 8-bit relative relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
/*!
*!reloc-rel-word [off] 16-bit relative section-crossing relocation
*! warns that a 16-bit relative relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
/*!
*!reloc-rel-dword [off] 32-bit relative section-crossing relocation
*! warns that a 32-bit relative relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
/*!
*!reloc-rel-qword [off] 64-bit relative section-crossing relocation
*! warns that an 64-bit relative relocation that could
*! not be resolved at assembly time was generated in
*! the output format.
*!
*! This is usually normal, but may not be handled by all
*! possible target environments
*/
int warn;
const char *type;
switch (data->type) {
case OUT_ADDRESS:
type = "absolute";
switch (asize) {
case 1: warn = WARN_RELOC_ABS_BYTE; break;
case 2: warn = WARN_RELOC_ABS_WORD; break;
case 4: warn = WARN_RELOC_ABS_DWORD; break;
case 8: warn = WARN_RELOC_ABS_QWORD; break;
default: panic();
}
break;
case OUT_RELADDR:
type = "relative";
switch (asize) {
case 1: warn = WARN_RELOC_REL_BYTE; break;
case 2: warn = WARN_RELOC_REL_WORD; break;
case 4: warn = WARN_RELOC_REL_DWORD; break;
case 8: warn = WARN_RELOC_REL_QWORD; break;
default: panic();
}
break;
default:
warn = 0;
}
if (warn) {
nasm_warn(warn, "%u-bit %s section-crossing relocation",
(unsigned int)(asize << 3), type);
}
}
break;
case OUT_SEGMENT:
nasm_assert(data->size <= 8);
asize = data->size;
amax = 2;
break;
default:
asize = amax = 0; /* Not an address */
break;
}
/*
* If the source location or output segment has changed,
* let the debug backend know. Some backends really don't
* like being given a NULL filename as can happen if we
* use -Lb and expand a macro, so filter out that case.
*/
data->where = src_where();
if (data->where.filename &&
(!src_location_same(data->where, dbg.where) |
(data->loc.segment != dbg.segment))) {
dbg.where = data->where;
dbg.segment = data->loc.segment;
dfmt->linenum(dbg.where.filename, dbg.where.lineno, data->loc.segment);
}
if (asize > amax) {
if (data->type == OUT_RELADDR || (data->flags & OUT_SIGNED)) {
nasm_nonfatal("%u-bit signed relocation unsupported by output format %s",
(unsigned int)(asize << 3), ofmt->shortname);
} else {
/*!
*!zext-reloc [on] relocation zero-extended to match output format
*! warns that a relocation has been zero-extended due
*! to limitations in the output format.
*/
nasm_warn(WARN_ZEXT_RELOC,
"%u-bit %s relocation zero-extended from %u bits",
(unsigned int)(asize << 3),
data->type == OUT_SEGMENT ? "segment" : "unsigned",
(unsigned int)(amax << 3));
}
zeropad = data->size - amax;
data->size = amax;
}
lfmt->output(data);
if (likely(data->loc.segment != NO_SEG)) {
/*
* Collect macro-related information for the debugger, if applicable
*/
if (debug_current_macro)
debug_macro_out(data);
if (unlikely(data->type == OUT_ZERODATA) &&
!(ofmt->flags & OFMT_ZERODATA)) {
/*
* Break OFMT_ZERODATA up into ZERO_BUF_SIZE chunks unless the
* backend has indicated it can handle arbitrary sizes
* by setting the OFMT_ZERODATA flag.
*/
uint64_t size = data->size;
while (size > ZERO_BUF_SIZE) {
data->type = OUT_ZERODATA; /* Help the compiler? */
data->size = ZERO_BUF_SIZE;
nasm_ofmt_output(data);
size -= ZERO_BUF_SIZE;
data->loc.offset += ZERO_BUF_SIZE;
data->insoffs += ZERO_BUF_SIZE;
}
data->size = size;
}
nasm_ofmt_output(data);
} else {
/* Outputting to ABSOLUTE section - only reserve is permitted */
if (data->type != OUT_RESERVE)
nasm_nonfatal("attempt to assemble code in [ABSOLUTE] space");
/* No need to push to the backend */
}
/* Prepare for the next instruction */
data->loc.offset += data->size;
data->insoffs += data->size;
/* Note: this is never called with zeropad > ZERO_BUF_SIZE */
if (zeropad) {
data->type = OUT_ZERODATA;
data->size = zeropad;
lfmt->output(data);
nasm_ofmt_output(data);
data->loc.offset += zeropad;
data->insoffs += zeropad;
}
/* Restore real data size in case the transaction was broken up */
data->size = real_size;
/* Avoid confusion when this struct out_data is reused */
data->what = NULL;
}
static inline void out_rawdata(struct out_data *data, const void *rawdata,
size_t size)
{
if (!size)
return;
data->type = OUT_RAWDATA;
data->data = rawdata;
data->size = size;
out(data);
}
static void out_rawbyte(struct out_data *data, uint8_t byte)
{
data->type = OUT_RAWDATA;
data->data = &byte;
data->size = 1;
out(data);
}
#if 0 /* Currently unused */
static void out_rawword(struct out_data *data, uint16_t value)
{
uint16_t buf = htole16(value);
data->type = OUT_RAWDATA;
data->data = &buf;
data->size = 2;
out(data);
}
#endif
static void out_rawdword(struct out_data *data, uint32_t value)
{
uint32_t buf = htole32(value);
data->type = OUT_RAWDATA;
data->data = &buf;
data->size = 4;
out(data);
}
static inline void out_reserve(struct out_data *data, uint64_t size)
{
data->type = OUT_RESERVE;
data->size = size;
out(data);
}
/*
* Emit a segment from a far expression. In this case, the segment offset is
* always zero. out_imm() handles explicit segment references, which may
* have addends.
*/
static void out_farseg(struct out_data *data, const struct operand *opx)
{
if (opx->opflags & OPFLAG_RELATIVE)
nasm_nonfatal("segment references cannot be relative");
data->type = OUT_SEGMENT;
data->flags = OUT_UNSIGNED;
data->size = 2;
data->toffset = 0;
data->tsegment = ofmt->segbase(opx->segment | 1);
data->twrt = opx->wrt;
out(data);
}
static void out_imm(struct out_data *data, const struct operand *opx,
int size, enum out_flags sign)
{
if (opx->segment != NO_SEG && (opx->segment & 1)) {
/*
* This is actually a segment reference, but eval() has
* already called ofmt->segbase() for us. Sigh.
*/
if (size < 2)
nasm_nonfatal("segment reference must be 16 bits");
data->type = OUT_SEGMENT;
} else {
data->type = (opx->opflags & OPFLAG_RELATIVE)
? OUT_RELADDR : OUT_ADDRESS;
}
data->flags = sign;
data->toffset = opx->offset;
data->tsegment = opx->segment;
data->twrt = opx->wrt;
/*
* XXX: improve this if at some point in the future we can
* distinguish the subtrahend in expressions like [foo - bar]
* where bar is a symbol in the current segment. However, at the
* current point, if OPFLAG_RELATIVE is set that subtraction has
* already occurred.
*/
data->relbase = 0;
data->size = size;
out(data);
}
static void out_reladdr(struct out_data *data, const struct operand *opx,
int size)
{
if (opx->opflags & OPFLAG_RELATIVE)
nasm_nonfatal("invalid use of self-relative expression");
data->type = OUT_RELADDR;
data->flags = OUT_SIGNED;
data->size = size;
data->toffset = opx->offset;
data->tsegment = opx->segment;
data->twrt = opx->wrt;
data->relbase = data->loc.offset + (data->inslen - data->insoffs);
out(data);
}
/* This is a real hack. The jcc8 or jmp8 byte code must come first. */
static enum match_result jmp_match(const struct itemplate *temp, const insn *ins)
{
const struct operand * const op0 = get_operand_const(ins, 0);
int64_t delta;
if (op0->type & STRICT)
return MERR_INVALOP;
if (unlikely(ins->opt & (OPTIM_NO_Jcc_RELAX|OPTIM_NO_JMP_RELAX))) {
const uint8_t c = temp->code[0];
switch (c) {
case 0370:
if (ins->opt & OPTIM_NO_Jcc_RELAX)
return MERR_INVALOP;
break;
case 0371:
if (ins->opt & OPTIM_NO_JMP_RELAX)
return MERR_INVALOP;
break;
default:
return MERR_INVALOP;
}
}
if (op0->opflags & OPFLAG_UNKNOWN) {
/* Be optimistic in pass 1 */
return MOK_GOOD;
}
if (op0->segment != ins->loc.segment) {
/* Cross-segment jump */
return MERR_INVALOP;
}
/*
* An instruction with a rel8 operand can only range between 2 and
* 15 bytes. If that is guaranteed true or false, there is no
* reason to go through the process of calculating the exact
* instruction size.
*
* Note that the instruction size is to be *subtracted* from the
* initial (beginning-of-instruction) delta value.
*/
delta = op0->offset - ins->loc.offset;
if (delta - 2 < -128 || delta - 15 > 127) {
/* This cannot be a byte-sized jump */
return MERR_INVALOP;
} else if (delta - 15 >= -128 && delta - 2 <= 127) {
/* It is guaranteed to be a valid byte-sized jump, no need to test */
} else {
/*
* Need to do this the hard way.
*
* However, calcsize() can modify the instruction structure,
* but after a mismatch we have to revert to the original
* state, so make a copy here and hold error messages.
*/
int64_t isize;
insn tmpins;
errhold hold;
tmpins = *ins;
tmpins.dummy = true;
hold = nasm_error_hold_push();
isize = calcsize(&tmpins, temp);
if (nasm_error_hold_pop(hold, false) >= ERR_NONFATAL)
return MERR_INVALOP;
if (isize < 0)
return MERR_INVALOP;
delta -= isize;
if ((int8_t)delta != delta)
return MERR_INVALOP;
}
return MOK_GOOD;
}
static inline int64_t merge_resb(insn *ins, int64_t isize)
{
int nbytes = resb_bytes(ins->opcode);
if (likely(!nbytes))
return isize;
if (isize != nbytes * ins->oprs[0].offset)
return isize; /* Has prefixes of some sort */
ins->oprs[0].offset *= ins->times;
isize *= ins->times;
ins->times = 1;
return isize;
}
/* This must be handle non-power-of-2 alignment values */
static inline size_t pad_bytes(size_t len, size_t align)
{
size_t partial = len % align;
return partial ? align - partial : 0;
}
static void out_eops(struct out_data *data, const extop *e)
{
while (e) {
size_t dup = e->dup;
switch (e->type) {
case EOT_NOTHING:
break;
case EOT_EXTOP:
while (dup--)
out_eops(data, e->val.subexpr);
break;
case EOT_DB_NUMBER:
if (e->elem > 8) {
nasm_nonfatal("integer supplied as %d-bit data",
e->elem << 3);
} else {
while (dup--) {
data->insoffs = 0;
data->inslen = data->size = e->elem;
data->tsegment = e->val.num.segment;
data->toffset = e->val.num.offset;
data->twrt = e->val.num.wrt;
data->relbase = 0;
if (e->val.num.segment != NO_SEG &&
(e->val.num.segment & 1)) {
data->type = OUT_SEGMENT;
data->flags = OUT_UNSIGNED;
} else {
data->type = e->val.num.relative
? OUT_RELADDR : OUT_ADDRESS;
data->flags = OUT_WRAP;
}
out(data);
}
}
break;
case EOT_DB_FLOAT:
case EOT_DB_STRING:
case EOT_DB_STRING_FREE:
{
size_t pad, len;
pad = pad_bytes(e->val.string.len, e->elem);
len = e->val.string.len + pad;
while (dup--) {
data->insoffs = 0;
data->inslen = len;
out_rawdata(data, e->val.string.data, e->val.string.len);
if (pad)
out_rawdata(data, zero_buffer, pad);
}
break;
}
case EOT_DB_RESERVE:
data->insoffs = 0;
data->inslen = dup * e->elem;
out_reserve(data, data->inslen);
break;
}
e = e->next;
}
}
/* This is totally just a wild guess what is reasonable... */
#define INCBIN_MAX_BUF (ZERO_BUF_SIZE * 16)
static int64_t assemble(insn *instruction)
{
struct out_data data;
const struct itemplate *temp;
enum match_result m;
const int64_t start = instruction->loc.offset;
const int bits = instruction->bits;
if (instruction->opcode == I_none)
return 0;
nasm_zero(data);
data.loc = instruction->loc;
data.bits = instruction->bits;
if (opcode_is_db(instruction->opcode)) {
out_eops(&data, instruction->eops);
} else if (instruction->opcode == I_INCBIN) {
const char *fname = instruction->eops->val.string.data;
FILE *fp;
size_t t = instruction->times; /* INCBIN handles TIMES by itself */
off_t base = 0;
off_t len;
const void *map = NULL;
char *buf = NULL;
size_t blk = 0; /* Buffered I/O block size */
size_t m = 0; /* Bytes last read */
if (!t)
goto done;
fp = nasm_open_read(fname, NF_BINARY|NF_FORMAP);
if (!fp) {
nasm_nonfatal("`incbin': unable to open file `%s'",
fname);
goto done;
}
len = nasm_file_size(fp);
if (len == (off_t)-1) {
nasm_nonfatal("`incbin': unable to get length of file `%s'",
fname);
goto close_done;
}
if (instruction->eops->next) {
base = instruction->eops->next->val.num.offset;
if (base >= len) {
len = 0;
} else {
len -= base;
if (instruction->eops->next->next &&
len > (off_t)instruction->eops->next->next->val.num.offset)
len = (off_t)instruction->eops->next->next->val.num.offset;
}
}
lfmt->set_offset(data.loc.offset);
lfmt->uplevel(LIST_INCBIN, len);
if (!len)
goto end_incbin;
/* Try to map file data */
map = nasm_map_file(fp, base, len);
if (!map) {
blk = len < (off_t)INCBIN_MAX_BUF ? (size_t)len : INCBIN_MAX_BUF;
buf = nasm_malloc(blk);
}
while (t--) {
/*
* Consider these irrelevant for INCBIN, since it is fully
* possible that these might be (way) bigger than an int
* can hold; there is, however, no reason to widen these
* types just for INCBIN. data.inslen == 0 signals to the
* backend that these fields are meaningless, if at all
* needed.
*/
data.insoffs = 0;
data.inslen = 0;
if (map) {
out_rawdata(&data, map, len);
} else if ((off_t)m == len) {
out_rawdata(&data, buf, len);
} else {
off_t l = len;
if (fseeko(fp, base, SEEK_SET) < 0 || ferror(fp)) {
nasm_nonfatal("`incbin': unable to seek on file `%s'",
fname);
goto end_incbin;
}
while (l > 0) {
m = fread(buf, 1, l < (off_t)blk ? (size_t)l : blk, fp);
if (!m || feof(fp)) {
/*
* This shouldn't happen unless the file
* actually changes while we are reading
* it.
*/
nasm_nonfatal("`incbin': unexpected EOF while"
" reading file `%s'", fname);
goto end_incbin;
}
out_rawdata(&data, buf, m);
l -= m;
}
}
}
end_incbin:
lfmt->downlevel(LIST_INCBIN);
if (instruction->times > 1) {
lfmt->uplevel(LIST_TIMES, instruction->times);
lfmt->downlevel(LIST_TIMES);
}
if (ferror(fp)) {
nasm_nonfatal("`incbin': error while"
" reading file `%s'", fname);
}
close_done:
if (buf)
nasm_free(buf);
if (map)
nasm_unmap_file(map, len);
fclose(fp);
done:
instruction->times = 1; /* Tell the upper layer not to iterate */
;
} else {
/* "Real" instruction */
/* Pre-match instruction structure update */
insn_early_setup(instruction);
m = find_match(&temp, instruction);
if (m >= MOK_GOOD) {
/* Matches! */
if (unlikely(itemp_has(temp, IF_OBSOLETE))) {
errflags warning;
const char *whathappened;
const char *validity;
bool never = itemp_has(temp, IF_NEVER);
/*
* If IF_OBSOLETE is set, warn the user. Different
* warning classes for "obsolete but valid for this
* specific CPU" and "obsolete and gone."
*
*!obsolete-removed [on] instruction obsolete and removed on the target CPU
*! warns for an instruction which has been removed
*! from the architecture, and is no longer included
*! in the CPU definition given in the \c{[CPU]}
*! directive, for example \c{POP CS}, the opcode for
*! which, \c{0Fh}, instead is an opcode prefix on
*! CPUs newer than the first generation 8086.
*
*!obsolete-nop [on] instruction obsolete and is a noop on the target CPU
*! warns for an instruction which has been removed
*! from the architecture, but has been architecturally
*! defined to be a noop for future CPUs.
*
*!obsolete-valid [on] instruction obsolete but valid on the target CPU
*! warns for an instruction which has been removed
*! from the architecture, but is still valid on the
*! specific CPU given in the \c{CPU} directive. Code
*! using these instructions is most likely not
*! forward compatible.
*/
whathappened = never ? "never implemented" : "obsolete";
if (!never && !iflag_cmp_cpu_level(&insns_flags[temp->iflag_idx], &cpu)) {
warning = WARN_OBSOLETE_VALID;
validity = "but valid on";
} else if (itemp_has(temp, IF_NOP)) {
warning = WARN_OBSOLETE_NOP;
validity = "and is a noop on";
} else {
warning = WARN_OBSOLETE_REMOVED;
validity = never ? "and invalid on" : "and removed from";
}
nasm_warn(warning, "instruction %s %s the target CPU",
whathappened, validity);
}
data.inslen = calcsize(instruction, temp);
nasm_assert(data.inslen >= 0);
data.inslen = merge_resb(instruction, data.inslen);
data.insoffs = 0;
gencode(&data, instruction);
if (unlikely(data.insoffs != data.inslen)) {
nasm_nonfatal("instruction length changed during code generation (%u -> %u)",
(unsigned int)data.insoffs, (unsigned int)data.inslen);
}
nasm_assert(data.loc.offset - start == data.inslen);
} else {
/* No match */
switch (m) {
case MERR_INVALOP:
default:
nasm_nonfatal("invalid combination of opcode and operands");
break;
case MERR_OPSIZEINVAL:
nasm_nonfatal("invalid operand sizes for instruction");
break;
case MERR_OPSIZEMISSING:
nasm_nonfatal("operation size not specified");
break;
case MERR_OPSIZEMISMATCH:
nasm_nonfatal("mismatch in operand sizes");
break;
case MERR_BRNOTHERE:
nasm_nonfatal("broadcast not permitted on this operand");
break;
case MERR_BRNUMMISMATCH:
nasm_nonfatal("mismatch in the number of broadcasting elements");
break;
case MERR_MASKNOTHERE:
nasm_nonfatal("mask not permitted on this operand");
break;
case MERR_DECONOTHERE:
nasm_nonfatal("unsupported mode decorator for instruction");
break;
case MERR_BADCPU:
nasm_nonfatal("no instruction for this cpu level");
break;
case MERR_BADMODE:
nasm_nonfatal("instruction not supported in %d-bit mode", bits);
break;
case MERR_ENCMISMATCH:
if (!instruction->prefixes[PPS_REX]) {
nasm_nonfatal("instruction not encodable without explicit prefix");
} else {
nasm_nonfatal("instruction not encodable with %s prefix",
prefix_name(instruction->prefixes[PPS_REX]));
}
break;
case MERR_BADBND:
case MERR_BADREPNE:
nasm_nonfatal("%s prefix is not allowed",
prefix_name(instruction->prefixes[PPS_REP]));
break;
case MERR_REGSETSIZE:
nasm_nonfatal("invalid register set size");
break;
case MERR_REGSET:
nasm_nonfatal("register set not valid for operand");
break;
case MERR_WRONGIMM:
nasm_nonfatal("operand/operator invalid for this instruction");
break;
case MERR_BADZU:
nasm_nonfatal("{zu} not applicable to this instruction");
break;
case MERR_MEMZU:
nasm_nonfatal("{zu} invalid for non-register destination");
break;
case MERR_BADNF:
nasm_nonfatal("{nf} not available for this instruction");
break;
case MERR_REQNF:
nasm_nonfatal("{nf} required for this instruction");
break;
}
instruction->times = 1; /* Avoid repeated error messages */
}
}
return data.loc.offset - start;
}
static int32_t eops_typeinfo(const extop *e)
{
int32_t typeinfo = 0;
while (e) {
switch (e->type) {
case EOT_NOTHING:
break;
case EOT_EXTOP:
typeinfo |= eops_typeinfo(e->val.subexpr);
break;
case EOT_DB_FLOAT:
switch (e->elem) {
case 1: typeinfo |= TY_BYTE; break;
case 2: typeinfo |= TY_WORD; break;
case 4: typeinfo |= TY_FLOAT; break;
case 8: typeinfo |= TY_QWORD; break; /* double? */
case 10: typeinfo |= TY_TBYTE; break; /* long double? */
case 16: typeinfo |= TY_YWORD; break;
case 32: typeinfo |= TY_ZWORD; break;
default: break;
}
break;
default:
switch (e->elem) {
case 1: typeinfo |= TY_BYTE; break;
case 2: typeinfo |= TY_WORD; break;
case 4: typeinfo |= TY_DWORD; break;
case 8: typeinfo |= TY_QWORD; break;
case 10: typeinfo |= TY_TBYTE; break;
case 16: typeinfo |= TY_YWORD; break;
case 32: typeinfo |= TY_ZWORD; break;
default: break;
}
break;
}
e = e->next;
}
return typeinfo;
}
static inline void debug_set_db_type(insn *instruction)
{
int32_t typeinfo = TYS_ELEMENTS(instruction->operands);
typeinfo |= eops_typeinfo(instruction->eops);
dfmt->debug_typevalue(typeinfo);
}
static void debug_set_type(insn *instruction)
{
int32_t typeinfo;
if (opcode_is_resb(instruction->opcode)) {
typeinfo = TYS_ELEMENTS(instruction->oprs[0].offset);
switch (instruction->opcode) {
case I_RESB:
typeinfo |= TY_BYTE;
break;
case I_RESW:
typeinfo |= TY_WORD;
break;
case I_RESD:
typeinfo |= TY_DWORD;
break;
case I_RESQ:
typeinfo |= TY_QWORD;
break;
case I_REST:
typeinfo |= TY_TBYTE;
break;
case I_RESO:
typeinfo |= TY_OWORD;
break;
case I_RESY:
typeinfo |= TY_YWORD;
break;
case I_RESZ:
typeinfo |= TY_ZWORD;
break;
default:
panic();
}
} else {
typeinfo = TY_LABEL;
}
dfmt->debug_typevalue(typeinfo);
}
/* Proecess an EQU directive */
static void define_equ(insn * instruction)
{
if (!instruction->label) {
nasm_nonfatal("EQU not preceded by label");
} else if (instruction->operands == 1 &&
(instruction->oprs[0].type & IMMEDIATE) &&
instruction->oprs[0].wrt == NO_SEG) {
define_label(instruction->label,
instruction->oprs[0].segment,
instruction->oprs[0].offset, false);
} else if (instruction->operands == 2
&& (instruction->oprs[0].type & IMMEDIATE)
&& (instruction->oprs[0].type & COLON)
&& instruction->oprs[0].segment == NO_SEG
&& instruction->oprs[0].wrt == NO_SEG
&& (instruction->oprs[1].type & IMMEDIATE)
&& instruction->oprs[1].segment == NO_SEG
&& instruction->oprs[1].wrt == NO_SEG) {
define_label(instruction->label,
instruction->oprs[0].offset | SEG_ABS,
instruction->oprs[1].offset, false);
} else {
nasm_nonfatal("bad syntax for EQU");
}
}
static int64_t len_extops(const extop *e)
{
int64_t isize = 0;
size_t pad;
while (e) {
switch (e->type) {
case EOT_NOTHING:
break;
case EOT_EXTOP:
isize += e->dup * len_extops(e->val.subexpr);
break;
case EOT_DB_STRING:
case EOT_DB_STRING_FREE:
case EOT_DB_FLOAT:
pad = pad_bytes(e->val.string.len, e->elem);
isize += e->dup * (e->val.string.len + pad);
break;
case EOT_DB_NUMBER:
warn_overflow_const(e->val.num.offset, e->elem);
isize += e->dup * e->elem;
break;
case EOT_DB_RESERVE:
isize += e->dup * e->elem;
break;
}
e = e->next;
}
return isize;
}
static int64_t insn_size(insn *instruction)
{
const struct itemplate *temp;
enum match_result m;
int64_t isize = 0;
if (instruction->opcode == I_none) {
return 0;
} else if (instruction->opcode == I_EQU) {
define_equ(instruction);
return 0;
} else if (opcode_is_db(instruction->opcode)) {
isize = len_extops(instruction->eops);
debug_set_db_type(instruction);
return isize;
} else if (instruction->opcode == I_INCBIN) {
const extop *e = instruction->eops;
const char *fname = e->val.string.data;
off_t len;
len = nasm_file_size_by_path(fname);
if (len == (off_t)-1) {
nasm_nonfatal("`incbin': unable to get length of file `%s'",
fname);
return 0;
}
e = e->next;
if (e) {
if (len <= (off_t)e->val.num.offset) {
len = 0;
} else {
len -= e->val.num.offset;
e = e->next;
if (e && len > (off_t)e->val.num.offset) {
len = (off_t)e->val.num.offset;
}
}
}
len *= instruction->times;
instruction->times = 1; /* Tell the upper layer to not iterate */
return len;
} else {
/* Normal instruction, or RESx */
/* Pre-matching setup */
insn_early_setup(instruction);
m = find_match(&temp, instruction);
if (m < MOK_GOOD)
return -1; /* No match */
isize = calcsize(instruction, temp);
debug_set_type(instruction);
isize = merge_resb(instruction, isize);
return isize;
}
}
static void bad_hle_warn(const insn * ins, uint8_t hleok)
{
enum prefixes rep_pfx = ins->prefixes[PPS_REP];
enum whatwarn { w_none, w_lock, w_inval } ww;
static const enum whatwarn warn[2][4] =
{
{ w_inval, w_inval, w_none, w_lock }, /* XACQUIRE */
{ w_inval, w_none, w_none, w_lock }, /* XRELEASE */
};
unsigned int n;
n = (unsigned int)rep_pfx - P_XACQUIRE;
if (n > 1)
return; /* Not XACQUIRE/XRELEASE */
ww = warn[n][hleok];
if (!is_class(MEMORY, ins->oprs[0].type))
ww = w_inval; /* HLE requires operand 0 to be memory */
/*!
*!prefix-hle [on] invalid HLE prefix
*!=hle
*! warns about invalid use of the HLE \c{XACQUIRE} or \c{XRELEASE}
*! prefixes.
*/
switch (ww) {
case w_none:
break;
case w_lock:
if (ins->prefixes[PPS_LOCK] != P_LOCK) {
nasm_warn(WARN_PREFIX_HLE,
"%s with this instruction requires lock",
prefix_name(rep_pfx));
}
break;
case w_inval:
nasm_warn(WARN_PREFIX_HLE,
"%s invalid with this instruction",
prefix_name(rep_pfx));
break;
}
}
static int ea_evex_err(decoflags_t deco, unsigned int vlen, const char *why)
{
const char *what = deco & ER
? "embedded rounding" : "suppress all exceptions";
nasm_nonfatal("%s not possible for %d-bit vectors%s",
what, 128 << vlen, why);
return -1;
}
/* Handle EVEX flags at the time of EA processing */
static int ea_evex_flags(insn *ins, const struct operand *opy)
{
/* EVEX.b1 : evex_brerop contains the operand position */
const struct operand *op_er_sae = ins->evex_brerop;
const decoflags_t deco = op_er_sae ? op_er_sae->decoflags : 0;
if (deco & (ER | SAE)) {
if (!itemp_has(ins->itemp, IF_LIG)) {
const unsigned int vlen = (ins->evex & EVEX_P2RC) >> 29;
const char *why = "";
switch (vlen) {
case 2:
/* 512 bits, no special encoding */
break;
case 1:
/* 256 bits, encodable in AVX 10.2 if mod=3 */
if (!iflag_test(&cpu, IF_AVX10_2)) {
why = " without AVX 10.2";
} else {
ins->evex ^= EVEX_P1U;
break;
}
/* else fall through */
default:
/* Not encodable */
return ea_evex_err(deco, vlen, why);
}
}
ins->evex &= ~EVEX_P2RC;
ins->evex ^= EVEX_P2B;
if (op_er_sae->decoflags & ER) {
/* set EVEX.RC (rounding control) */
ins->evex ^= ((ins->evex_rm - BRC_RN) << 29) & EVEX_P2RC;
}
} else if (opy->decoflags & BRDCAST_MASK) {
/* set EVEX.b but don't EVEX.L/RC */
ins->evex ^= EVEX_P2B;
}
return 0;
}
static int ea_evex_post_check(const insn *ins)
{
const struct operand *op_er_sae = ins->evex_brerop;
const decoflags_t deco = op_er_sae ? op_er_sae->decoflags : 0;
if ((deco & (ER | SAE)) && !itemp_has(ins->itemp, IF_LIG)) {
const unsigned int vlen = (ins->evex & EVEX_P2RC) >> 29;
if (vlen == 1 && (ins->ea.modrm >> 6) != 3) {
return ea_evex_err(deco, vlen, " when accessing memory");
}
}
return 0;
}
/* Common construct */
#define case3(x) case (x): case (x)+1: case (x)+2
#define case4(x) case3(x): case (x)+3
static int64_t calcsize(insn *ins, const struct itemplate * const temp)
{
const int bits = ins->bits;
const uint8_t *codes = temp->code;
int64_t length = 0;
uint8_t c;
int op1, op2;
struct operand *opx, *opy;
uint8_t opex = 0;
enum ea_type eat;
uint8_t hleok = 0;
bool lockcheck = true;
enum reg_enum mib_index = R_none; /* For a separate index reg form */
const char *errmsg;
int need_pfx[MAXPREFIX];
int opt_opsize = 0;
ins->rex = 0; /* Ensure REX is reset */
ins->evex = 0; /* Ensure EVEX is reset */
ins->vexreg = 0; /* No V register */
ins->vex_cm = 0; /* No implicit map */
ins->bits = bits; /* Execution mode (default asize) */
ins->itemp = temp; /* Instruction template */
eat = EA_SCALAR; /* Expect a scalar EA */
/* Default operand size */
ins->op_size = bits != 16 ? 32 : 16;
nasm_zero(need_pfx);
while (*codes) {
c = *codes++;
op1 = (c & 3) + ((opex & 1) << 2);
opx = get_operand(ins, op1);
op2 = ((c >> 3) & 3) + ((opex & 2) << 1);
opy = NULL;
opex = 0; /* For the next iteration */
switch (c) {
case4(01):
codes += c, length += c;
break;
case3(05):
opex = c;
break;
case4(010):
ins->rex |= op_rexflags(opx, REX_rB);
codes++, length++;
break;
case4(014):
/* this is an index reg of a split SIB operand */
mib_index = opx->basereg;
break;
case4(020):
case4(024):
length++;
break;
case4(030):
length += 2;
break;
case4(034):
if (opx->type & (BITS16 | BITS32 | BITS64))
length += (opx->type & BITS16) ? 2 : 4;
else
length += (bits == 16) ? 2 : 4;
break;
case4(040):
length += 4;
break;
case4(044):
length += ins->addr_size >> 3;
break;
case4(050):
length++;
break;
case4(054):
length += 8; /* MOV reg64/imm */
break;
case4(060):
length += 2;
break;
case4(064):
if (opx->type & (BITS16 | BITS32 | BITS64))
length += (opx->type & BITS16) ? 2 : 4;
else
length += (bits == 16) ? 2 : 4;
break;
case4(070):
length += 4;
break;
case4(074):
length += 2;
break;
case 0171:
c = *codes++;
op2 = (op2 & ~3) | ((c >> 3) & 3);
opy = get_operand(ins, op2);
ins->rex |= op_rexflags(opy, REX_rR);
length++;
break;
case 0172:
case 0173:
codes++;
length++;
break;
case4(0174):
length++;
break;
case4(0240):
if (is_class(opx->type, IMMEDIATE))
ins->veximm = opx->offset;
else
ins->vexreg = regval(opx);
goto evex_common;
case 0250:
ins->vexreg = 0;
goto evex_common;
evex_common:
ins->rex |= REX_EV;
ins->evex = 0x62;
ins->evex += *codes++ << 8;
ins->evex += *codes++ << 16;
ins->evex += *codes++ << 24;
ins->evex_tuple = (*codes++ - 0300);
ins->vex_cm = ((ins->evex >> 8) & 7) | (RV_EVEX << 6);
break;
case4(0254):
case4(0264):
length += 4;
break;
case4(0260):
if (is_class(opx->type, IMMEDIATE))
ins->veximm = opx->offset;
else
ins->vexreg = regval(opx);
goto vex_common;
case 0270:
ins->vexreg = 0;
goto vex_common;
vex_common:
ins->rex |= REX_V;
ins->vex_cm = *codes++;
ins->vex_wlp = *codes++;
break;
case3(0271):
hleok = c & 3;
break;
case4(0274):
length++;
break;
case4(0300):
length++;
break;
case4(0304):
length++;
codes += 2;
break;
case 0310:
{
enum prefixes pfx = ins->prefixes[PPS_ASIZE];
ins->addr_size = 16;
if (bits == 64)
return -1;
if (pfx) {
if (!(pfx == P_A16 || (bits == 32 && pfx == P_ASP)))
return -1;
} else {
ins->prefixes[PPS_ASIZE] = P_A16;
}
break;
}
case 0311:
{
enum prefixes pfx = ins->prefixes[PPS_ASIZE];
ins->addr_size = 32;
if (pfx) {
if (!(pfx == P_A32 || (bits != 32 && pfx == P_ASP)))
return -1;
} else {
ins->prefixes[PPS_ASIZE] = P_A32;
}
break;
}
case 0312:
break;
case 0313:
{
enum prefixes pfx = ins->prefixes[PPS_ASIZE];
ins->addr_size = 64;
if (bits != 64 || (pfx && pfx != P_A64))
return -1;
else
ins->prefixes[PPS_ASIZE] = P_A64;
break;
}
case4(0314):
break;
case 0320:
is_o16:
{
/*! prefix-opsize [on] invalid operand size prefix
*! warns that an operand prefix (\c{o16}, \c{o32}, \c{o64},
*! \c{osp}) invalid for the specified instruction has been specified.
*! The operand prefix will be ignored by the assembler.
*/
enum prefixes pfx = ins->prefixes[PPS_OSIZE];
ins->op_size = 16;
if (bits != 16 && pfx == P_OSP) {
/* Allow osp prefix as is */
} else if (pfx != P_none && pfx != P_O16) {
nasm_warn(WARN_PREFIX_OPSIZE,
"invalid operand size prefix %s, must be o16",
prefix_name(pfx));
} else if (!(opt_opsize & 16)) {
ins->prefixes[PPS_OSIZE] = P_O16;
}
break;
}
case 0321:
is_o32:
{
enum prefixes pfx = ins->prefixes[PPS_OSIZE];
ins->op_size = 32;
if (ins->rex & REX_NW) {
nasm_nonfatalf(ERR_PASS2,
"instruction not valid with 32-bit operand size");
return -1;
} else if (bits == 16 && pfx == P_OSP) {
/* Allow osp prefix as is */
} else if (pfx != P_none && pfx != P_O32) {
nasm_warn(WARN_PREFIX_OPSIZE,
"invalid operand size prefix %s, must be o32",
prefix_name(pfx));
} else if (!(opt_opsize & 32)) {
ins->prefixes[PPS_OSIZE] = P_O32;
}
break;
}
case 0322:
break;
case 0327:
if (bits == 64) {
ins->rex |= REX_NW;
if (ins->op_size == 32)
ins->op_size = 64;
}
break;
case 0323:
ins->rex |= REX_NW;
/* fall through */
case 0324:
is_o64:
{
enum prefixes pfx = ins->prefixes[PPS_OSIZE];
ins->op_size = 64;
if (pfx == P_OSP) {
/* If osp is specified, leave it, but force REX.W */
if (!(ins->rex & REX_NW))
ins->rex |= REX_W;
} else if (pfx != P_none && pfx != P_O64) {
nasm_warn(WARN_PREFIX_OPSIZE,
"invalid operand size prefix %s, must be o64",
prefix_name(pfx));
} else if (!(opt_opsize & 64)) {
ins->prefixes[PPS_OSIZE] = P_O64;
}
break;
}
case 0325:
ins->rex |= REX_NH;
break;
case 0326:
break;
case 0330:
switch (ins->prefixes[PPS_OSIZE]) {
case P_none:
switch (ins->op_size) {
case 16: goto is_o16;
case 32: goto is_o32;
case 64: goto is_o64;
default: panic(); break;
}
break;
case P_OSP:
if (bits == 16)
goto is_o32;
else
goto is_o16;
case P_O16:
goto is_o16;
case P_O32:
goto is_o32;
case P_O64:
goto is_o64;
default:
panic();
break;
}
break;
case 0331:
ins->need_pfx[PPS_REP] = PFE_NULL;
break;
case 0332:
ins->need_pfx[PPS_REP] = 0xf2;
break;
case 0333:
ins->need_pfx[PPS_REP] = 0xf3;
break;
case 0334:
ins->rex |= REX_L; /* Ignored in 64-bit mode */
break;
case 0335:
break;
case 0336:
if (!(optimizing & OPTIM_STRICT_OSIZE))
opt_opsize = 16|32|64;
break;
case 0337:
if (!(optimizing & OPTIM_STRICT_OSIZE))
opt_opsize = 64;
break;
case 0340:
/*!
*!forward [on] forward reference may have unpredictable results
*! warns that a forward reference is used which may have
*! unpredictable results, notably in a \c{RESB}-type
*! pseudo-instruction. These would be \i\e{critical
*! expressions} (see \k{crit}) but are permitted in a
*! handful of cases for compatibility with older
*! versions of NASM. This warning should be treated as a
*! severe programming error as the code could break at
*! any time for any number of reasons.
*/
/* The bytecode ends in 0, so opx points to operand 0 */
if (!absolute_op(opx)) {
nasm_nonfatal("attempt to reserve non-constant"
" quantity of memory");
return -1;
} else if (opx->opflags & OPFLAG_FORWARD) {
nasm_warn(WARN_FORWARD, "forward reference in %s "
"can have unpredictable results",
nasm_insn_names[ins->opcode]);
}
length += opx->offset * resb_bytes(ins->opcode);
break;
case 0341:
if (!ins->prefixes[PPS_WAIT])
ins->prefixes[PPS_WAIT] = P_WAIT;
break;
case 0344:
ins->rex |= REX_P | REX_B;
break;
case 0345:
ins->rex |= REX_P | REX_X;
break;
case 0346:
ins->rex |= REX_P | REX_R;
break;
case 0347:
ins->rex |= REX_P | REX_W;
break;
case 0350:
ins->rex |= REX_P | REX_2;
break;
case 0351:
ins->rex |= REX_P | REX_2 | REX_W;
break;
case3(0355):
/* Set opmap for legacy and REX2 encodings */
ins->vex_cm = c & 3;
break;
case 0360:
ins->need_pfx[PPS_REP] = PFE_NULL;
ins->need_pfx[PPS_OSIZE] = PFE_NULL;
break;
case 0361:
ins->need_pfx[PPS_REP] = PFE_NULL;
/* fall through */
case 0366:
ins->need_pfx[PPS_OSIZE] = 0x66;
break;
case 0364:
ins->need_pfx[PPS_OSIZE] = PFE_NULL;
break;
case 0365:
ins->need_pfx[PPS_ASIZE] = PFE_NULL;
break;
case 0367:
ins->need_pfx[PPS_ASIZE] = 0x67;
break;
case 0370:
break;
case 0371:
if (ins->prefixes[PPS_REP] == P_BND) {
/* jmp short (opcode eb) cannot be used with bnd prefix. */
ins->prefixes[PPS_REP] = P_none;
/*!
*!prefix-bnd [on] invalid \c{BND} prefix
*!=bnd
*! warns about ineffective use of the \c{BND} prefix when the
*! \c{JMP} instruction is converted to the \c{SHORT} form.
*! This should be extremely rare since the short \c{JMP} only
*! is applicable to jumps inside the same module, but if
*! it is legitimate, it may be necessary to use
*! \c{bnd jmp dword}.
*/
if (!ins->dummy)
nasm_warn(WARN_PREFIX_BND,
"jmp short does not init bnd regs - bnd prefix dropped");
}
break;
case 0373:
length++;
break;
case 0374:
eat = EA_XMMVSIB;
break;
case 0375:
eat = EA_YMMVSIB;
break;
case 0376:
eat = EA_ZMMVSIB;
break;
case4(0100):
case4(0110):
case4(0120):
case4(0130):
case4(0200):
case4(0204):
case4(0210):
case4(0214):
case4(0220):
case4(0224):
case4(0230):
case4(0234):
{
int rfield;
opflags_t rflags;
opy = get_operand(ins, op2);
ins->ea.rex = 0; /* Ensure ea.REX is initially 0 */
if (c <= 0177) {
/* pick rfield from operand b (opx) */
rflags = regflag(opx);
rfield = nasm_regvals[opx->basereg];
} else {
rflags = 0;
rfield = c & 7;
opx = NULL;
}
if (itemp_has(temp, IF_MIB)) {
opy->eaflags |= EAF_MIB;
/*
* if a separate form of MIB (ICC style) is used,
* the index reg info is merged into mem operand
*/
if (mib_index != R_none) {
opy->indexreg = mib_index;
opy->scale = 1;
opy->hintbase = mib_index;
opy->hinttype = EAH_NOTBASE;
}
}
/* SIB encoding required */
if (itemp_has(temp, IF_SIB))
opy->eaflags |= EAF_SIB;
/* ea_evex_flags() must come before process_ea() */
if (ea_evex_flags(ins, opy))
return -1;
if (process_ea(opy, rfield, rflags, ins, eat, &errmsg)) {
nasm_nonfatal("%s", errmsg);
return -1;
}
ins->rex |= ins->ea.rex;
length += ins->ea.size;
if (ea_evex_post_check(ins))
return -1;
}
break;
default:
nasm_panic("internal instruction table corrupt"
": instruction code \\%o (0x%02X) given", c, c);
break;
}
}
if (ins->rex & REX_NH) {
if (ins->rex & REX_H) {
nasm_nonfatal("instruction cannot use high registers");
return -1;
}
ins->rex &= ~REX_P; /* Don't force REX prefix due to high reg */
}
if (ins->prefixes[PPS_OSIZE] == P_O64) {
ins->rex |= !(ins->rex & REX_NW) ? REX_W : 0;
}
switch (ins->prefixes[PPS_REX]) {
case P_EVEX:
if (!(ins->rex & REX_EV))
return -1;
break;
case P_VEX:
case P_VEX3:
case P_VEX2:
if (!(ins->rex & REX_V))
return -1;
break;
case P_REX:
if (bits != 64) {
nasm_nonfatal("REX encoding not supported in %d-bit mode", bits);
return -1;
}
if (ins->rex & (REX_V|REX_EV|REX_2))
return -1;
ins->rex |= REX_P; /* Force REX prefix */
break;
case P_REX2:
if (bits != 64) {
nasm_nonfatal("REX2 encoding not supported in %d-bit mode", bits);
return -1;
}
if ((ins->rex & (REX_V|REX_EV)) || rex_highvec(ins->rex))
return -1;
ins->rex |= REX_P | REX_2; /* Force REX2 prefix */
break;
default:
break;
}
if (ins->rex & (REX_V | REX_EV)) {
uint32_t bad32 = REX_BXR;
if (ins->rex & REX_H) {
nasm_nonfatal("cannot use high byte register in this instruction");
return -1;
}
if (itemp_has(temp, IF_WW)) {
bad32 |= REX_W;
} else {
ins->rex = (ins->rex & ~REX_W) | ((ins->vex_wlp >> (7-3)) & REX_W);
}
if (bits != 64 && ((ins->rex & bad32) || ins->vexreg > 7)) {
nasm_nonfatal("invalid operands in non-64-bit mode");
return -1;
}
if (ins->rex & REX_EV) {
/* EVEX */
length += 4;
} else {
/* VEX */
if (ins->vexreg > 15 || (ins->rex & REX_BXR1)) {
nasm_nonfatal("invalid high-16 register in VEX encoded instruction");
return -1;
}
if (ins->vex_cm != 1 ||
(ins->rex & (REX_B|REX_X|REX_W)) ||
ins->prefixes[PPS_REX] == P_VEX3) {
/* VEX3 required */
if (ins->prefixes[PPS_REX] == P_VEX2)
nasm_nonfatal("instruction not encodable with {vex2} prefix");
length += 3;
} else {
/* VEX2 available */
length += 2;
}
}
} else if (ins->rex & (REX_BXR1 | REX_2)) {
/* REX2 prefix needed */
if (bits != 64) {
nasm_nonfatal("invalid operands in %d-bit mode", bits);
return -1;
}
if (!iflag_test(&cpu, IF_APX)) {
nasm_nonfatal("invalid operands in non-APX mode");
return -1;
}
if (itemp_has(temp, IF_NOAPX) || rex_highvec(ins->rex)) {
nasm_nonfatal("this use of registers 16-31 not supported for this instruction");
return -1;
}
if (ins->rex & REX_H) {
nasm_nonfatal("cannot use high byte register in this instruction");
return -1;
}
ins->rex |= REX_2 | REX_P;
length += 2;
} else if (ins->rex & REX_MASK) {
if (ins->rex & REX_H) {
nasm_nonfatal("cannot use high byte register in this instruction");
return -1;
} else if (bits == 64) {
ins->rex &= ~REX_L;
ins->rex |= REX_P;
} else if ((ins->rex & (REX_L|REX_W|REX_BXR)) == (REX_L|REX_R) &&
iflag_cpu_level_ok(&cpu, IF_X86_64)) {
/* LOCK-as-REX.R */
if (assert_no_prefix(ins, PPS_LOCK))
return -1;
lockcheck = false; /* Already errored, no need for warning */
ins->rex &= ~REX_P;
} else {
nasm_nonfatal("invalid operands in %d-bit mode", bits);
return -1;
}
length++;
}
if (!(ins->rex & (REX_2|REX_V|REX_EV))) {
/* Opmap legacy encoding: none, 0f, 0f 38, 0f 3a */
length += (ins->vex_cm > 0) + (ins->vex_cm > 1);
}
if (lockcheck && has_prefix(ins, PPS_LOCK, P_LOCK)) {
if ((!itemp_has(temp,IF_LOCK) || !is_class(MEMORY, ins->oprs[0].type)) &&
(!itemp_has(temp,IF_LOCK1) || !is_class(MEMORY, ins->oprs[1].type))) {
/*!
*!prefix-lock-error [on] \c{LOCK} prefix on unlockable instruction
*!=lock
*! warns about \c{LOCK} prefixes on unlockable instructions.
*/
nasm_warn(WARN_PREFIX_LOCK_ERROR, "instruction is not lockable");
} else if (temp->opcode == I_XCHG) {
/*!
*!prefix-lock-xchg [on] superfluous \c{LOCK} prefix on \c{XCHG} instruction
*! warns about a \c{LOCK} prefix added to an \c{XCHG} instruction.
*! The \c{XCHG} instruction is \e{always} locking, and so this
*! prefix is not necessary; however, NASM will generate it if
*! explicitly provided by the user, so this warning indicates that
*! suboptimal code is being generated.
*/
nasm_warn(WARN_PREFIX_LOCK_XCHG,
"superfluous LOCK prefix on XCHG instruction");
}
}
bad_hle_warn(ins, hleok);
/*
* when BND prefix is set by DEFAULT directive,
* BND prefix is added to every appropriate instruction line
* unless it is overridden by NOBND prefix.
*/
if (globl.bnd &&
(itemp_has(temp, IF_BND) && !has_prefix(ins, PPS_REP, P_NOBND)))
ins->prefixes[PPS_REP] = P_BND;
/*
* Warn on {pt} or {pn} on a nonhintable instruction
*/
if (ins->prefixes[PPS_SEG] == P_PT || ins->prefixes[PPS_SEG] == P_PN) {
if (!itemp_has(temp, IF_JCC_HINT)) {
/*!
*!prefix-hint-dropped [on] invalid branch hint prefix dropped
*! warns that the \c{{PT}} (predict taken) or \c{{PN}}
*! (predict not taken) branch prediction hint prefixes
*! are specified on an instruction that does not take
*! these prefixes. As these prefixes alias the segment
*! override prefixes, this may be a very serious error,
*! and therefore NASM will not generate these prefixes.
*! To force these prefixes to be emitted, use \c{DS} or
*! \c{CS}, instead, respectively.
*/
nasm_warn(WARN_PREFIX_HINT_DROPPED,
"invalid branch hint prefix dropped");
ins->prefixes[PPS_SEG] = 0;
}
}
/*
* Add length of legacy prefixes
*/
length += emit_prefixes(NULL, ins);
return length;
}
/* Emit REX and REX2 prefixes, and if necessary legacy opmap bytes */
static inline void emit_rex(struct out_data *data, insn *ins)
{
uint8_t buf[4];
size_t n = 0;
if (ins->rex_done)
return;
ins->rex_done = true;
if (ins->rex & (REX_V | REX_EV))
return; /* Handled elsewhere */
if (ins->rex & REX_2) {
buf[0] = 0xd5;
buf[1] = ins->rex & (REX_BXR0|REX_W);
buf[1] |= (ins->rex & REX_BXR1) >> 8;
buf[1] |= ins->vex_cm << 7;
n = 2;
} else {
uint8_t rex = ins->rex & (REX_MASK|REX_L);
if (rex == (REX_L|REX_R)) {
buf[n++] = 0xf0;
} else if (rex & REX_P) {
buf[n++] = rex;
} else {
nasm_assert(!(rex & ~REX_L));
}
if (ins->vex_cm) {
buf[n++] = 0x0f;
if (ins->vex_cm > 1) {
/* Map 2 = 0F 38, map 3 = 0F 3A */
buf[n++] = 0x34 + (ins->vex_cm << 1);
}
}
}
out_rawdata(data, buf, n);
}
/*
* Return the byte value of a legacy prefix (possibly depending on context)
* Returns one of the enum prefix_err values if the prefix has no byte value.
*/
static int prefix_byte(enum prefixes pfx, const int bits)
{
switch ((int)pfx) {
case P_WAIT:
return 0x9B;
case P_LOCK:
return 0xF0;
case P_REPNE:
case P_REPNZ:
case P_XACQUIRE:
case P_BND:
return 0xF2;
case P_REPE:
case P_REPZ:
case P_REP:
case P_XRELEASE:
return 0xF3;
case R_CS:
case P_PN:
return 0x2E;
case R_DS:
case P_PT:
return 0x3E;
case R_ES:
return 0x26;
case R_FS:
return 0x64;
break;
case R_GS:
return 0x65;
break;
case R_SS:
return 0x36;
break;
case P_A16:
if (bits == 64)
return PFE_ERR;
else if (bits == 32)
return 0x67;
else
return PFE_NULL;
case P_A32:
return (bits != 32) ? 0x67 : PFE_NULL;
case P_ASP:
return 0x67;
case P_O16:
return (bits != 16) ? 0x66 : PFE_NULL;
case P_O32:
return (bits == 16) ? 0x66 : PFE_NULL;
case P_O64:
return (bits == 64) ? PFE_NULL : PFE_ERR;
case P_OSP:
return 0x66;
case P_REX:
case P_VEX:
case P_EVEX:
case P_VEX3:
case P_VEX2:
case P_REX2:
return PFE_MULTI;
case P_NF:
case P_NOBND:
case P_ZU:
case P_none:
return PFE_NULL;
default:
return PFE_WHAT;
}
}
/* Emit legacy prefixes */
static int emit_prefixes(struct out_data *data, const insn *ins)
{
const int bits = ins->bits;
uint8_t buf[MAXPREFIX];
int bytes = 0;
int j;
bool do_warn;
/*
* Note: do not issue warnings if data is NULL during the
* data-generation pass, or the warnings will be issued twice.
* Warnings are still issued on previous passes, in case we
* terminate with an error.
*/
do_warn = !pass_final() || data;
for (j = 0; j < MAXPREFIX; j++) {
const enum prefixes pfx = ins->prefixes[j];
const int r = ins->need_pfx[j];
int c = prefix_byte(pfx, bits);
if (r && r != c && !(r <= 0 && c <= 0)) {
switch (pfx) {
case P_none:
case P_O16:
case P_O32:
case P_O64:
case P_A16:
case P_A32:
case P_A64:
/* In these cases, allow the instruction to simply override */
c = r;
break;
default:
/*!
*!prefix-invalid [on] invalid prefix for instruction
*! this instruction is only valid with certain combinations
*! of prefixes. The prefix will still be generated as
*! requested, but the result may be a completely different
*! instruction.
*/
if (do_warn)
nasm_warn(WARN_PREFIX_INVALID,
"invalid prefix %s for instruction, result may be unexpected",
prefix_name(pfx));
break;
}
}
/* Various warnings and error conditions */
switch ((int)pfx) {
case R_CS:
case R_DS:
/* Hintable Jcc instructions OK even in 64-bit mode */
if (itemp_has(ins->itemp, IF_JCC_HINT))
break;
/* fall through */
case R_ES:
case R_SS:
/*!
*!prefix-seg [on] segment prefix ignored in 64-bit mode
*! warns that an \c{es}, \c{cs}, \c{ss} or \c{ds} segment override
*! prefix has no effect in 64-bit mode. The prefix will still be
*! generated as requested.
*/
if (do_warn && bits == 64) {
nasm_warn(WARN_PREFIX_SEG,
"%s segment override will be ignored in 64-bit mode",
prefix_name(pfx));
}
break;
case P_A16:
if (bits == 64)
nasm_nonfatal("16-bit addressing is not supported "
"in 64-bit mode");
break;
case P_A64:
if (bits != 64)
nasm_nonfatal("64-bit addressing is only supported "
"in 64-bit mode");
break;
case P_O64:
if (bits != 64)
nasm_nonfatal("64-bit operand size is only supported "
"in 64-bit mode");
break;
default:
switch (c) {
case 0x66:
case 0xf2:
case 0xf3:
/* These prefixes are embedded in a VEX/EVEX prefix */
if (ins->rex & (REX_V|REX_EV))
c = PFE_NULL;
break;
case PFE_ERR:
nasm_nonfatal("invalid use of %s prefix", prefix_name(pfx));
break;
case PFE_WHAT:
nasm_nonfatal("%s cannot be used as a prefix", prefix_name(pfx));
break;
default:
break;
}
}
if (c >= 0)
buf[bytes++] = c;
}
if (data && bytes)
out_rawdata(data, buf, bytes);
return bytes;
}
static void gencode(struct out_data *data, insn *ins)
{
uint8_t c;
uint8_t bytes[4];
int64_t size;
int op1, op2;
struct operand *opx, *opy;
const uint8_t *codes = ins->itemp->code;
uint8_t opex = 0;
int r;
const int bits = ins->bits;
data->itemp = ins->itemp;
data->bits = ins->bits;
ins->rex_done = false;
emit_prefixes(data, ins);
while (*codes) {
c = *codes++;
op1 = (c & 3) + ((opex & 1) << 2);
opx = get_operand(ins, op1);
op2 = ((c >> 3) & 3) + ((opex & 2) << 1);
opy = NULL;
opex = 0; /* For the next iteration */
switch (c) {
case 01:
case 02:
case 03:
case 04:
emit_rex(data, ins);
out_rawdata(data, codes, c);
codes += c;
break;
case 05:
case 06:
case 07:
opex = c;
break;
case4(010):
emit_rex(data, ins);
out_rawbyte(data, *codes++ + (regval(opx) & 7));
break;
case4(014):
break;
case4(020):
out_imm(data, opx, 1, OUT_WRAP);
break;
case4(024):
out_imm(data, opx, 1, OUT_UNSIGNED);
break;
case4(030):
out_imm(data, opx, 2, OUT_WRAP);
break;
case4(034):
if (opx->type & (BITS16 | BITS32))
size = (opx->type & BITS16) ? 2 : 4;
else
size = (bits == 16) ? 2 : 4;
out_imm(data, opx, size, OUT_WRAP);
break;
case4(040):
out_imm(data, opx, 4, OUT_WRAP);
break;
case4(044):
size = ins->addr_size >> 3;
out_imm(data, opx, size, OUT_WRAP);
break;
case4(050):
if (opx->segment == data->loc.segment) {
int64_t delta = sext(opx->offset - data->loc.offset
- (data->inslen - data->insoffs),
ins->op_size);
if (delta != (int8_t)delta)
nasm_nonfatal("short jump is out of range");
}
out_reladdr(data, opx, 1);
break;
case4(054):
out_imm(data, opx, 8, OUT_WRAP);
break;
case4(060):
out_reladdr(data, opx, 2);
break;
case4(064):
if (opx->type & (BITS16 | BITS32 | BITS64))
size = (opx->type & BITS16) ? 2 : 4;
else
size = (bits == 16) ? 2 : 4;
out_reladdr(data, opx, size);
break;
case4(070):
out_reladdr(data, opx, 4);
break;
case4(074):
if (opx->segment == NO_SEG)
nasm_nonfatal("value referenced by FAR is not relocatable");
out_farseg(data, opx);
break;
case 0171:
c = *codes++;
op2 = (op2 & ~3) | ((c >> 3) & 3);
opx = &ins->oprs[op2];
r = nasm_regvals[opx->basereg];
c = (c & ~070) | ((r & 7) << 3);
out_rawbyte(data, c);
break;
case 0172:
{
int mask = ins->prefixes[PPS_REX] == P_EVEX ? 7 : 15;
const struct operand *opy;
c = *codes++;
opx = get_operand(ins, op1 = (c >> 3) & 7);
opy = get_operand(ins, op2 = c & 7);
if (!absolute_op(opy))
nasm_nonfatal("non-absolute expression not permitted "
"as argument %d", op2);
else if (opy->offset & ~mask)
nasm_warn(WARN_NUMBER_OVERFLOW,
"is4 argument exceeds bounds");
c = opy->offset & mask;
goto emit_is4;
}
case 0173:
c = *codes++;
opx = get_operand(ins, op1 = c >> 4);
c &= 15;
goto emit_is4;
case4(0174):
c = 0;
emit_is4:
r = nasm_regvals[opx->basereg];
out_rawbyte(data, (r << 4) | ((r & 0x10) >> 1) | c);
break;
case4(0240):
case 0250:
{
uint8_t vregbits = ins->vexreg | ins->veximm;
codes += 4;
ins->evex ^= op_evexflags(&ins->oprs[0], EVEX_P2Z|EVEX_P2AAA);
ins->evex ^= (ins->rex & REX_BXR0) << 13; /* R0 X0 B0 */
if (ins->rex & REX_B1)
ins->evex ^= (ins->rex & REX_BV) ? EVEX_P0X : EVEX_P0BP;
if (ins->rex & REX_X1)
ins->evex ^= (ins->rex & REX_XV) ? EVEX_P2VP : EVEX_P1XP;
if (ins->rex & REX_R1)
ins->evex ^= EVEX_P0RP;
if (ins->rex & REX_W)
ins->evex |= EVEX_P1W; /* OR is correct here */
ins->evex ^= (vregbits & 15) << 19;
ins->evex ^= (vregbits & 16) << (27 - 4);
if (ins->prefixes[PPS_NF] == P_NF) {
/* Set NF if {nd} and this is applicable */
if (itemp_has(ins->itemp, IF_NF_E))
ins->evex ^= EVEX_P2NF;
/* Clear NF if the instruction forbids it */
if (itemp_has(ins->itemp, IF_NF_N)) {
nasm_warn(WARN_OTHER, "this instruction doesn't allow {nf}");
}
}
/* Set ND if {zu} and this is applicable */
if (ins->prefixes[PPS_ZU] == P_ZU) {
if (itemp_has(ins->itemp, IF_ZU_E))
ins->evex ^= EVEX_P2ND;
}
out_rawdword(data, ins->evex);
break;
}
case4(0254):
out_imm(data, opx, 4, OUT_SIGNED);
break;
case4(0264):
out_imm(data, opx, 4, OUT_UNSIGNED);
break;
case4(0260):
case 0270:
codes += 2;
if (ins->vex_cm != 1 ||
(ins->rex & (REX_W|REX_X|REX_B)) ||
ins->prefixes[PPS_REX] == P_VEX3) {
bytes[0] = (ins->vex_cm >> 6) ? 0x8f : 0xc4;
bytes[1] = (ins->vex_cm & 31) |
((~ins->rex & REX_BXR0) << 5);
bytes[2] = ((ins->rex & REX_W) << (7-3)) |
((~ins->vexreg & 15) << 3) |
(ins->vex_wlp & 07);
out_rawdata(data, bytes, 3);
} else {
bytes[0] = 0xc5;
bytes[1] = ((~ins->rex & REX_R) << (7-2)) |
((~ins->vexreg & 15) << 3) |
(ins->vex_wlp & 07);
out_rawdata(data, bytes, 2);
}
break;
case 0271:
case 0272:
case 0273:
break;
case4(0274):
{
uint64_t uv, um;
int s;
if (absolute_op(opx)) {
s = ins->op_size;
um = (uint64_t)2 << (s-1);
uv = opx->offset;
if (uv > 127 && uv < (uint64_t)-128 &&
(uv < um-128 || uv > um-1)) {
/*
* If this wasn't explicitly byte-sized, warn as though we
* had fallen through to the imm16/32/64 case.
*/
nasm_warn(WARN_NUMBER_OVERFLOW|ERR_PASS2,
"%s exceeds bounds",
(opx->type & BITS8) ? "signed byte" :
s == 16 ? "word" :
s == 32 ? "dword" :
"signed dword");
}
/* Output as a raw byte to avoid byte overflow check */
out_rawbyte(data, (uint8_t)uv);
} else {
out_imm(data, opx, 1, OUT_WRAP); /* XXX: OUT_SIGNED? */
}
break;
}
case4(0300):
{
emit_rex(data, ins);
if (absolute_op(opx)) {
if (!is_hint_nop(opx->offset)) {
nasm_warn(0, "not a valid hint-NOP opcode (0D, 18-1F)");
}
out_rawbyte(data, opx->offset);
} else {
out_imm(data, opx, 1, OUT_UNSIGNED);
}
break;
}
case4(0304):
{
uint8_t type = *codes++;
uint8_t modifier = *codes++;
enum out_flags flags = type & 3; /* signed or unsigned */
nasm_assert(type <= 2);
nasm_assert(absolute_op(opx));
warn_overflow_out(opx->offset, 1, flags, "byte");
out_rawbyte(data, opx->offset ^ modifier);
break;
}
case 0310:
case 0311:
break;
case 0312:
break;
case 0313:
break;
case4(0314):
break;
case 0320:
case 0321:
break;
case 0322:
case 0323:
case 0324:
case 0327:
break;
case 0325:
break;
case 0326:
break;
case 0330:
case 0331:
case 0332:
case 0333:
break;
case 0334:
break;
case 0335:
break;
case 0336:
case 0337:
break;
case 0340:
if (opx->segment != NO_SEG)
nasm_panic("non-constant BSS size in pass two");
out_reserve(data, ins->oprs[0].offset * resb_bytes(ins->opcode));
break;
case 0341:
break;
case4(0344):
break;
case 0350:
case 0351:
break;
case3(0355):
break;
case 0360:
break;
case 0361:
break;
case 0364:
case 0365:
break;
case 0366:
case 0367:
break;
case3(0370):
break;
case 0373:
out_rawbyte(data, bits == 16 ? 3 : 5);
break;
case 0374:
case 0375:
case 0376:
break;
case4(0100):
case4(0110):
case4(0120):
case4(0130):
case4(0200):
case4(0204):
case4(0210):
case4(0214):
case4(0220):
case4(0224):
case4(0230):
case4(0234):
{
uint8_t *p;
bool overflow;
opy = get_operand(ins, op2);
p = bytes;
*p++ = ins->ea.modrm;
if (ins->ea.sib_present)
*p++ = ins->ea.sib;
out_rawdata(data, bytes, p - bytes);
/*
* Make sure the address gets the right offset in case
* the line breaks in the .lst file (BR 1197827)
*/
switch (ins->ea.bytes) {
case 0:
/* No displacement */
overflow = false;
break;
case 1:
/* 8-bit displacement, which may be compressed */
out_rawbyte(data, ins->ea.disp8);
overflow = !ins->ea.disp8_ok;
break;
default:
if (ins->ea.rip) {
out_reladdr(data, opy, ins->ea.bytes);
overflow = false;
} else {
unsigned int asize = ins->addr_size >> 3;
out_imm(data, opy, ins->ea.bytes,
(asize > ins->ea.bytes)
? OUT_SIGNED|OUT_NOWARN : OUT_WRAP|OUT_NOWARN);
overflow = overflow_general(opy->offset, asize) ||
sext(opy->offset, ins->addr_size) !=
sext(opy->offset, ins->ea.bytes << 3);
}
}
if (overflow)
warn_overflow(ins->ea.bytes, NULL, "displacement");
}
break;
default:
nasm_panic("internal instruction table corrupt"
": instruction code \\%o (0x%02X) given", c, c);
break;
}
}
}
static opflags_t regflag(const operand *o)
{
if (!is_register(o->basereg))
nasm_panic("invalid operand passed to regflag()");
return nasm_reg_flags[o->basereg];
}
static int32_t regval(const operand *o)
{
/*
* Certain instruction patterns allow an immediate to be put
* into a register field
*/
if (o->type & IMMEDIATE)
return o->offset;
if (!is_register(o->basereg))
nasm_panic("invalid operand passed to regval()");
return nasm_regvals[o->basereg];
}
static uint32_t op_rexflags(const operand * o, uint32_t mask)
{
opflags_t flags;
int val;
if (!is_register(o->basereg))
nasm_panic("invalid operand passed to op_rexflags()");
flags = nasm_reg_flags[o->basereg];
val = nasm_regvals[o->basereg];
return rexflags(val, flags, mask);
}
static uint32_t rexflags(int val, opflags_t flags, uint32_t mask)
{
uint32_t rex = 0;
if (val < 0 || !is_class(REGISTER, flags)) {
/* Not a register */
return 0;
}
if (val & 8)
rex |= REX_B|REX_X|REX_R;
if (val & 16)
rex |= REX_B1|REX_X1|REX_R1;
if (flags & REG_CLASS_VECTOR) {
rex |= REX_BV|REX_XV|REX_RV;
} else if (is_class(REG8, flags)) {
if (is_class(REG_HIGH, flags)) {
/* AH, CH, DH, BH: REX/REX2/VEX/EVEX forbidden */
rex |= REX_H;
} else if (val >= 4) {
/* SPL, BPL, SIL, DIL, or extended: prefix required */
rex |= REX_P;
}
}
return rex & mask;
}
static uint32_t evexflags(decoflags_t deco, uint32_t mask)
{
uint32_t evex = 0;
if (deco & Z)
evex |= EVEX_P2Z;
if (deco & OPMASK_MASK)
evex |= (deco << 24) & EVEX_P2AAA;
return evex & mask;
}
static uint32_t op_evexflags(const operand * o, uint32_t mask)
{
return evexflags(o->decoflags, mask);
}
static enum match_result find_match(const struct itemplate **tempp,
insn *instruction)
{
const int bits = instruction->bits;
const struct itemplate_list *templist;
const struct itemplate *temp;
const struct itemplate *best = NULL;
enum match_result m, merr;
int this_good;
int rex = instruction->prefixes[PPS_REX];
unsigned int n;
/* Impossible encoding request? */
if (bits != 64) {
if (rex == P_REX || rex == P_REX2)
return MERR_ENCMISMATCH;
}
merr = MERR_INVALOP;
best = NULL;
this_good = 0;
templist = &nasm_instructions[instruction->opcode];
n = templist->ntemp;
temp = templist->temp;
while (n--) {
m = matches(temp, instruction);
if (m > merr) {
best = temp;
merr = m;
this_good = 1;
if (merr == MOK_GOOD)
break;
} else if (m == merr) {
this_good++;
}
temp++;
}
if (merr >= MOK_FIRST)
merr = MOK_GOOD; /* Fuzzy match but valid */
*tempp = best;
return merr;
}
static uint8_t get_broadcast_num(opflags_t opflags, opflags_t brsize)
{
unsigned int opsize = (opflags & SIZE_MASK) >> SIZE_SHIFT;
uint8_t brcast_num;
if (brsize > BITS64)
nasm_fatal("size of broadcasting element is greater than 64 bits");
/*
* The shift term is to take care of the extra BITS80 inserted
* between BITS64 and BITS128.
*/
brcast_num = ((opsize / (BITS64 >> SIZE_SHIFT)) * (BITS64 / brsize))
>> (opsize > (BITS64 >> SIZE_SHIFT));
return brcast_num;
}
static enum match_result matches(const struct itemplate * const itemp,
const insn * const ins)
{
const int bits = ins->bits;
int i;
const int oprs = ins->operands;
unsigned int arflag;
unsigned int armask, smmask;
bool if_anysize, if_sx;
int op_size;
opflags_t opsize, arsize, smsize;
opflags_t isize[MAX_OPERANDS]; /* Adjusted instruction operand sizes */
opflags_t tsize[MAX_OPERANDS]; /* Adjusted template operand sizes */
opflags_t itype[MAX_OPERANDS]; /* Adjusted instruction flags */
bool sm_missing;
/* Jump size/type declarators are more like types than sizes */
const opflags_t jsize_mask = NEAR|FAR|SHORT|ABS;
const opflags_t msize_mask = SIZE_MASK & ~jsize_mask;
/*
* Check the opcode
*/
if (itemp->opcode != ins->opcode)
return MERR_INVALOP;
/*
* Count the operands
*/
if (itemp->operands != oprs)
return MERR_INVALOP;
/*
* Is it legal?
*/
if (unlikely(ins->opt & OPTIM_STRICT_INSTR)) {
if (itemp_has(itemp, IF_OPT))
return MERR_INVALOP;
}
/*
* {rex/vexn/evex} available?
*/
switch (ins->prefixes[PPS_REX]) {
case P_EVEX:
if (!itemp_has(itemp, IF_EVEX))
return MERR_ENCMISMATCH;
break;
case P_VEX:
case P_VEX3:
case P_VEX2:
if (!itemp_has(itemp, IF_VEX))
return MERR_ENCMISMATCH;
break;
case P_REX:
if (bits != 64 ||
itemp_has(itemp, IF_NOREX) ||
itemp_has(itemp, IF_VEX) ||
itemp_has(itemp, IF_EVEX) ||
itemp_has(itemp, IF_REX2))
return MERR_ENCMISMATCH;
break;
case P_REX2:
if (bits != 64 ||
itemp_has(itemp, IF_NOAPX) ||
itemp_has(itemp, IF_EVEX))
return MERR_ENCMISMATCH;
break;
default:
if (itemp_has(itemp, IF_EVEX)) {
if (!iflag_test(&cpu, IF_EVEX))
return MERR_BADCPU;
} else if (itemp_has(itemp, IF_VEX)) {
if (!iflag_test(&cpu, IF_VEX)) {
return MERR_BADCPU;
} else if (itemp_has(itemp, IF_LATEVEX)) {
if (!iflag_test(&cpu, IF_LATEVEX) && iflag_test(&cpu, IF_EVEX))
return MERR_BADCPU;
} else if (itemp_has(itemp, IF_REX2)) {
if (bits != 64 || !iflag_test(&cpu, IF_APX))
return MERR_BADCPU;
}
}
break;
}
/*
* First, cursory operand filtering and initialize
* itype[] and isize[]
*/
for (i = 0; i < oprs; i++) {
const struct operand * const op = &ins->oprs[i];
const opflags_t ttype = itemp->opd[i];
isize[i] = op->type & msize_mask;
itype[i] = op->type - isize[i];
if (op->type & ~ttype & (COLON | TO))
return MERR_INVALOP;
if (op->iflag && !itemp_has(itemp, op->iflag))
return MERR_WRONGIMM;
}
/* "Default" operand size (from mode and prefixes only) */
op_size = ins->op_size;
if (itemp_has(itemp, IF_NWSIZE) && op_size == 32) {
/* If this is an nw instruction, default to 64 bits in 64-bit mode */
op_size = bits;
}
opsize = mode_to_op(op_size);
/* Some key flags */
if_anysize = itemp_has(itemp, IF_ANYSIZE);
if_sx = itemp_has(itemp, IF_SX);
/*
* Compare various operand flags that don't depend on sizes,
* and compute the "true" template operand sizes.
*/
for (i = 0; i < oprs; i++) {
const opflags_t ttype = itemp->opd[i];
const decoflags_t deco = ins->oprs[i].decoflags;
const decoflags_t ideco = itemp->deco[i];
const bool is_broadcast = deco & BRDCAST_MASK;
if (likely(!is_broadcast)) {
tsize[i] = ttype & msize_mask;
} else {
const decoflags_t ideco_brsize = ideco & BRSIZE_MASK;
if (~ideco & BRDCAST_MASK)
return MERR_BRNOTHERE;
/*
* when broadcasting, the element size depends on
* the instruction type. decorator flag should match.
*/
if (ideco_brsize)
tsize[i] = brsize_to_size(ideco_brsize);
else
tsize[i] = 0; /* Is this even possible? */
}
/* Handle implied SHORT or NEAR */
if (unlikely(ttype & (NEAR|SHORT))) {
if ((ttype & (NEAR|SHORT)) == (NEAR|SHORT)) {
/* Only a short form exists; allow both NEAR and SHORT */
if (!(itype[i] & (FAR|ABS)))
itype[i] |= NEAR|SHORT;
} else if ((itype[i] & SHORT) || isize[i] == BITS8) {
/* An explicit SHORT or BITS8 cancel NEAR; are synonyms */
itype[i] &= ~NEAR;
if (!isize[i])
isize[i] = BITS8;
} else if (!(itype[i] & (FAR|ABS|SHORT))) {
/* NEAR is implicit unless otherwise specified */
itype[i] |= ttype & NEAR;
}
}
/* Check to see if we need to coerce an explicitly sized immediate */
if (unlikely(itype[i] & ttype & IMMEDIATE)) {
/*
* If this is an *explicitly* sized immediate,
* allow it to match an extending pattern.
*/
switch (isize[i]) {
case BITS8:
if (ttype & BYTEEXTMASK) {
isize[i] = tsize[i];
itype[i] |= BYTEEXTMASK;
}
break;
case BITS32:
if (ttype & DWORDEXTMASK)
isize[i] = tsize[i];
break;
default:
break;
}
/*
* MOST instructions which take an sdword64 are the only form;
* set the SDWORD flag to be strict about sdword64 matching
* (use when a true imm64 pattern exists.)
*/
if (!itemp_has(itemp, IF_SDWORD))
itype[i] |= SDWORD;
}
if (ttype & ~itype[i] & ~(msize_mask|REGSET_MASK))
return MERR_INVALOP;
if (~ideco & deco & OPMASK_MASK)
return MERR_MASKNOTHERE;
if (~ideco & deco & (Z_MASK|STATICRND_MASK|SAE_MASK))
return MERR_DECONOTHERE;
if (~ttype & itype[i] & REGSET_MASK)
return (ttype & REGSET_MASK) ? MERR_REGSETSIZE : MERR_REGSET;
}
/*
* Process the operand sizes.
*/
arflag = itemp_smask(itemp);
nasm_static_assert(SIZE_SHIFT >= IF_SB);
nasm_static_assert((BITS8 >> SIZE_SHIFT) == 1);
arsize = (arflag & IF_TSMASK) << (SIZE_SHIFT - IF_SB);
if (arflag & IFM_OSIZE)
arsize = opsize;
else if (arflag & IFM_ASIZE)
arsize = mode_to_op(ins->addr_size);
/* Flags for which the AR and SM flags apply */
armask = itemp_arx(itemp);
smmask = itemp_smx(itemp);
/* Apply ARx and register default sizes */
if (!if_sx) {
for (i = 0; i < oprs; i++) {
const unsigned int bit = 1U << i;
if (!isize[i]) {
if (is_class(REGISTER, itype[i]) && tsize[i])
isize[i] = tsize[i];
else if (armask & bit)
isize[i] = arsize;
}
}
}
/*
* Look the common subset for the size-matched operands.
* However, defer the error messages to until after
* the final operand checking loop, to get a better
* order of message priorities.
*/
smsize = msize_mask;
sm_missing = false;
if (smmask) {
unsigned int nosizemask = 0;
for (i = 0; i < oprs; i++) {
const unsigned int bit = 1U << i;
if (isize[i]) {
if (smmask & bit)
smsize &= isize[i];
} else if (tsize[i] && !is_class(REGISTER, itype[i])) {
nosizemask |= bit;
}
}
sm_missing = !(smmask & ~nosizemask);
}
/*
* Check that the operand sizes actually match,
* and look for other kinds of mismatches, e.g.
* REG_HIGH when REX is specified.
*/
for (i = 0; i < oprs; i++) {
const opflags_t type = itype[i];
const decoflags_t deco = ins->oprs[i].decoflags;
const decoflags_t ideco = itemp->deco[i];
const bool is_broadcast = deco & BRDCAST_MASK;
const bool has_ar = (armask >> i) & 1;
const bool has_sm = (smmask >> i) & 1;
const bool is_reg = is_class(REGISTER, type);
/*
* Operand sizes are considered matching at this stage
* if any one of these is true:
*
* 1. The template size is undefined.
* XXX: apply ARx|Sx flags here?
* 2. The operand size matches the template size.
* 3. There is an ARx|ANYSIZE flag in the template.
* 4. The operand size is unspecified and the
* template does not carry an ARx|SX flag.
*/
if (tsize[i]) {
if (isize[i] != tsize[i]) {
if (has_ar) {
if (if_anysize)
goto isize_ok;
if (unlikely(if_sx) && !is_reg)
return MERR_OPSIZEINVAL;
}
if (isize[i])
return is_reg ? MERR_INVALOP : MERR_OPSIZEINVAL;
}
isize_ok:
if (has_sm)
smsize &= tsize[i];
} else {
/*
* SX with an unsized operand means only an unsized operand
* is OK.
*/
if (unlikely(if_sx) && has_ar)
if (isize[i])
return MERR_OPSIZEINVAL;
}
if (is_class(REG_HIGH, type) && ins->prefixes[PPS_REX]) {
/* High registers cannot be combined with any REX type */
return MERR_INVALOP;
}
if (is_broadcast) {
/* calculate the proper number : {1to<brcast_num>} */
const decoflags_t ideco_brsize = ideco & BRSIZE_MASK;
unsigned int brcast_num = 0;
if (ideco_brsize)
brcast_num = get_broadcast_num(itemp->opd[i], tsize[i]);
if (brcast_num != (2U << ((deco & BRNUM_MASK) >> BRNUM_SHIFT))) {
/*
* broadcasting opsize matches but the number of repeated memory
* element does not match.
* if 64b double precision float is broadcasted to ymm (256b),
* broadcasting decorator must be {1to4}.
*/
return MERR_BRNUMMISMATCH;
}
}
}
/*
* Make sure that our size match set, if any, did not get exhausted,
* and contains exactly one valid combination still...
*/
if (smmask) {
if (!smsize)
return MERR_OPSIZEMISMATCH;
else if (!is_power2(smsize) || (if_sx && sm_missing))
return MERR_OPSIZEMISSING;
}
/*
* Check template is okay at the set cpu level
*/
if (iflag_cmp_cpu_level(&insns_flags[itemp->iflag_idx], &cpu) > 0)
return MERR_BADCPU;
/*
* Verify the appropriate long mode flag.
*/
if (itemp_has(itemp, (bits == 64 ? IF_NOLONG : IF_LONG)))
return MERR_BADMODE;
/*
* If we have a HLE prefix, look for the NOHLE flag
*/
if (itemp_has(itemp, IF_NOHLE) &&
(has_prefix(ins, PPS_REP, P_XACQUIRE) ||
has_prefix(ins, PPS_REP, P_XRELEASE)))
return MERR_BADHLE;
/*
* {nf} or {zu} prefixes used? Must be permitted.
*/
if (has_prefix(ins, PPS_NF, P_NF)) {
if (!itemp_has(itemp, IF_NF) &&
(itemp_has(itemp, IF_FL) ||
(ins->opt & OPTIM_STRICT_INSTR)))
return MERR_BADNF;
} else if (itemp_has(itemp, IF_NF_R)) {
return MERR_REQNF;
}
if (has_prefix(ins, PPS_ZU, P_ZU)) {
if (!itemp_has(itemp, IF_ZU))
return MERR_BADZU;
else if (!(ins->oprs[0].type & REGISTER))
return MERR_MEMZU;
}
/*
* Check if BND prefix is allowed.
* Other 0xF2 (REPNE/REPNZ) prefix is prohibited.
*/
if (!itemp_has(itemp, IF_BND) &&
(has_prefix(ins, PPS_REP, P_BND) ||
has_prefix(ins, PPS_REP, P_NOBND)))
return MERR_BADBND;
else if (itemp_has(itemp, IF_BND) &&
(has_prefix(ins, PPS_REP, P_REPNE) ||
has_prefix(ins, PPS_REP, P_REPNZ)))
return MERR_BADREPNE;
/*
* Check if special handling needed for relaxable jump
*/
if (itemp_has(itemp, IF_JMP_RELAX))
return jmp_match(itemp, ins);
return MOK_GOOD;
}
/*
* Select the mod part of modr/m for an memory operand with displacement.
* zerook should be clear for the forbidden BP encodings; such instructions
* must be coded with a +0 displacement.
*
* 0 = no displacement
* 1 = 8-bit displacment
* 2 = 16/32-bit displacement
*/
static unsigned int memory_mod(const int eaflags, insn *ins, int64_t o,
bool known, bool zerook)
{
struct ea_data * const output = &ins->ea;
/* Explicitly requested by user */
if (eaflags & EAF_WORDOFFS) {
return 2;
}
output->disp8_shift = get_disp8_shift(ins);
output->disp8 = (int8_t)(o >> output->disp8_shift);
output->disp8_ok = known &&
((int64_t)output->disp8 << output->disp8_shift)
== sext(o, ins->addr_size);
/* Explicitly requested by user */
if (eaflags & EAF_BYTEOFFS) {
return 1;
}
/* Not knowable at this time */
if (!known)
return 2;
/* No displacement needed? */
if (o == 0 && zerook)
return 0;
/* 8-bit displacement possible? */
return output->disp8_ok ? 1 : 2;
}
static int process_ea(operand *input, int rfield, opflags_t rflags,
insn *ins, enum ea_type expected,
const char **errmsgp)
{
struct ea_data * const output = &ins->ea;
const bool long_mode = ins->bits == 64;
const int addrbits = ins->addr_size;
const int eaflags = input->eaflags;
const char *errmsg = NULL;
errmsg = NULL;
nasm_zero(*output);
output->type = EA_SCALAR;
/* REX flags for the rfield operand */
output->rex |= rexflags(rfield, rflags, REX_rR);
if (is_class(REGISTER, input->type)) {
/*
* It's a direct register.
*/
if (!is_register(input->basereg))
goto err;
if (!is_reg_class(REG_EA, input->basereg))
goto err;
/* broadcasting is not available with a direct register operand. */
if (input->decoflags & BRDCAST_MASK) {
errmsg = "broadcast not allowed with register operand";
goto err;
}
output->rex |= op_rexflags(input, REX_rB);
output->sib_present = false; /* no SIB necessary */
output->bytes = 0; /* no offset necessary either */
output->modrm = GEN_MODRM(3, rfield, nasm_regvals[input->basereg]);
} else {
/*
* It's a memory reference.
*/
/* Embedded rounding or SAE is not available with a mem ref operand. */
if (input->decoflags & (ER | SAE)) {
errmsg = "embedded rounding is available only with "
"register-register operations";
goto err;
}
if (input->basereg == -1 &&
(input->indexreg == -1 || input->scale == 0)) {
/*
* It's a pure offset. If it is an IMMEDIATE, it is a pattern
* in insns.dat which allows an immediate to be used as a memory
* address, in which case apply the default REL/ABS.
*/
if (long_mode) {
if (is_class(IMMEDIATE, input->type)) {
if (!(input->eaflags & EAF_ABS) &&
((input->eaflags & EAF_REL) || globl.rel))
input->type |= IP_REL;
}
if ((input->type & IP_REL) == IP_REL) {
/*!
*!ea-absolute [on] absolute address cannot be RIP-relative
*! warns that an address that is inherently absolute cannot
*! be generated with RIP-relative encoding using \c{REL},
*! see \k{default-rel}.
*/
if (input->segment == NO_SEG ||
(input->opflags & OPFLAG_RELATIVE)) {
nasm_warn(WARN_EA_ABSOLUTE,
"absolute address can not be RIP-relative");
input->type &= ~IP_REL;
input->type |= MEMORY;
}
}
}
if (long_mode && !(IP_REL & ~input->type) && (eaflags & EAF_SIB)) {
errmsg = "instruction requires SIB encoding, cannot be RIP-relative";
goto err;
}
if ((eaflags & EAF_BYTEOFFS) ||
((eaflags & EAF_WORDOFFS) &&
input->disp_size != (addrbits != 16 ? 32 : 16))) {
/*!
*!ea-dispsize [on] displacement size ignored on absolute address
*! warns that NASM does not support generating displacements for
*! inherently absolute addresses that do not match the address size
*! of the instruction.
*/
nasm_warn(WARN_EA_DISPSIZE, "displacement size ignored on absolute address");
}
if ((eaflags & EAF_SIB) ||
(long_mode && (~input->type & IP_REL))) {
output->sib_present = true;
output->sib = GEN_SIB(0, 4, 5);
output->bytes = 4;
output->modrm = GEN_MODRM(0, rfield, 4);
output->rip = false;
} else {
output->sib_present = false;
output->bytes = (addrbits != 16 ? 4 : 2);
output->modrm = GEN_MODRM(0, rfield,
(addrbits != 16 ? 5 : 6));
output->rip = long_mode;
}
} else {
/*
* It's an indirection.
*/
int i = input->indexreg, b = input->basereg, s = input->scale;
const bool known =
!(input->opflags & OPFLAG_UNKNOWN) &&
(input->segment == NO_SEG);
int hb = input->hintbase, ht = input->hinttype;
int t, it, bt; /* register numbers */
opflags_t x, ix, bx; /* register flags */
if (s == 0)
i = -1; /* make this easy, at least */
if (is_register(i)) {
it = nasm_regvals[i];
ix = nasm_reg_flags[i];
} else {
it = -1;
ix = 0;
}
if (is_register(b)) {
bt = nasm_regvals[b];
bx = nasm_reg_flags[b];
} else {
bt = -1;
bx = 0;
}
/* if either one are a vector register... */
if ((ix|bx) & (XMMREG|YMMREG|ZMMREG) & ~REG_EA) {
opflags_t sok = BITS32 | BITS64;
int32_t o = input->offset;
int mod, scale, index, base;
/*
* For a vector SIB, one has to be a vector and the other,
* if present, a GPR. The vector must be the index operand.
*/
if (it == -1 || (bx & (XMMREG|YMMREG|ZMMREG) & ~REG_EA)) {
if (s == 0)
s = 1;
else if (s != 1)
goto err;
t = bt, bt = it, it = t;
x = bx, bx = ix, ix = x;
}
if (bt != -1) {
if (REG_GPR & ~bx)
goto err;
if (!(REG64 & ~bx) || !(REG32 & ~bx))
sok &= bx;
else
goto err;
}
/*
* While we're here, ensure the user didn't specify
* WORD or QWORD
*/
if (input->disp_size == 16 || input->disp_size == 64)
goto err;
if (addrbits == 16 ||
(addrbits == 32 && !(sok & BITS32)) ||
(addrbits == 64 && !(sok & BITS64)))
goto err;
output->type = ((ix & ZMMREG & ~REG_EA) ? EA_ZMMVSIB
: ((ix & YMMREG & ~REG_EA)
? EA_YMMVSIB : EA_XMMVSIB));
output->rex |= rexflags(it, ix, REX_rX);
output->rex |= rexflags(bt, bx, REX_rB);
index = it & 7; /* it is known to be != -1 */
switch (s) {
case 1:
scale = 0;
break;
case 2:
scale = 1;
break;
case 4:
scale = 2;
break;
case 8:
scale = 3;
break;
default: /* then what the smeg is it? */
goto err; /* panic */
}
if (bt == -1) {
base = 5;
mod = 0;
} else {
base = bt & 7;
mod = memory_mod(eaflags, ins, o, known,
base != REG_NUM_EBP);
}
output->sib_present = true;
output->bytes = (bt == -1 || mod == 2 ? 4 : mod);
output->modrm = GEN_MODRM(mod, rfield, 4);
output->sib = GEN_SIB(scale, index, base);
} else if ((ix|bx) & (BITS32|BITS64)) {
/*
* it must be a 32/64-bit memory reference. Firstly we have
* to check that all registers involved are type E/Rxx.
*/
opflags_t sok = BITS32 | BITS64;
int32_t o = input->offset;
if (it != -1) {
if (!(REG64 & ~ix) || !(REG32 & ~ix))
sok &= ix;
else
goto err;
}
if (bt != -1) {
if (REG_GPR & ~bx)
goto err; /* Invalid register */
if (~sok & bx & SIZE_MASK)
goto err; /* Invalid size */
sok &= bx;
}
/*
* While we're here, ensure the user didn't specify
* WORD or QWORD
*/
if (input->disp_size == 16 || input->disp_size == 64)
goto err;
if (addrbits == 16 ||
(addrbits == 32 && !(sok & BITS32)) ||
(addrbits == 64 && !(sok & BITS64)))
goto err;
/* now reorganize base/index */
if (s == 1 && bt != it && bt != -1 && it != -1 &&
((hb == b && ht == EAH_NOTBASE) ||
(hb == i && ht == EAH_MAKEBASE))) {
/* swap if hints say so */
t = bt, bt = it, it = t;
x = bx, bx = ix, ix = x;
}
if (bt == -1 && s == 1 && !(hb == i && ht == EAH_NOTBASE)) {
/* make single reg base, unless hint */
bt = it, bx = ix, it = -1, ix = 0;
}
if (eaflags & EAF_MIB) {
/* MIB/split-SIB encoding */
if (it == -1 && (hb == b && ht == EAH_NOTBASE)) {
/*
* make a single reg index [reg*1].
* gas uses this form for an explicit index register.
*/
it = bt, ix = bx, bt = -1, bx = 0, s = 1;
}
if ((ht == EAH_SUMMED) && bt == -1) {
/* separate once summed index into [base, index] */
bt = it, bx = ix, s--;
}
} else {
if (((s == 2 && it != REG_NUM_ESP &&
(!(eaflags & EAF_TIMESTWO) || (ht == EAH_SUMMED))) ||
s == 3 || s == 5 || s == 9) && bt == -1) {
/* convert 3*EAX to EAX+2*EAX */
bt = it, bx = ix, s--;
}
if (it == -1 && (bt & 7) != REG_NUM_ESP &&
(eaflags & EAF_TIMESTWO) &&
(hb == b && ht == EAH_NOTBASE)) {
/*
* convert [NOSPLIT EAX*1]
* to sib format with 0x0 displacement - [EAX*1+0].
*/
it = bt, ix = bx, bt = -1, bx = 0, s = 1;
}
}
if (s == 1 && it == REG_NUM_ESP) {
/* swap ESP into base if scale is 1 */
t = it, it = bt, bt = t;
x = ix, ix = bx, bx = x;
}
if (it == REG_NUM_ESP ||
(s != 1 && s != 2 && s != 4 && s != 8 && it != -1))
goto err; /* wrong, for various reasons */
output->rex |= rexflags(it, ix, REX_rX);
output->rex |= rexflags(bt, bx, REX_rB);
if (it == -1 && (bt & 7) != REG_NUM_ESP && !(eaflags & EAF_SIB)) {
/* no SIB needed */
int mod, rm;
if (bt == -1) {
rm = 5;
mod = 0;
} else {
rm = bt & 7;
mod = memory_mod(eaflags, ins, o, known,
rm != REG_NUM_EBP);
}
output->sib_present = false;
output->bytes = (bt == -1 || mod == 2 ? 4 : mod);
output->modrm = GEN_MODRM(mod, rfield, rm);
} else {
/* we need a SIB */
int mod, scale, index, base;
if (it == -1)
index = 4, s = 1;
else
index = (it & 7);
switch (s) {
case 1:
scale = 0;
break;
case 2:
scale = 1;
break;
case 4:
scale = 2;
break;
case 8:
scale = 3;
break;
default: /* then what the smeg is it? */
goto err; /* panic */
}
if (bt == -1) {
base = 5;
mod = 0;
} else {
base = (bt & 7);
mod = memory_mod(eaflags, ins, o, known,
base != REG_NUM_EBP);
}
output->sib_present = true;
output->bytes = (bt == -1 || mod == 2 ? 4 : mod);
output->modrm = GEN_MODRM(mod, rfield, 4);
output->sib = GEN_SIB(scale, index, base);
}
} else { /* it's 16-bit */
int mod, rm;
int16_t o = input->offset;
/* check for 64-bit long mode */
if (addrbits == 64)
goto err;
/* check all registers are BX, BP, SI or DI */
if ((b != -1 && b != R_BP && b != R_BX && b != R_SI && b != R_DI) ||
(i != -1 && i != R_BP && i != R_BX && i != R_SI && i != R_DI))
goto err;
/* ensure the user didn't specify DWORD/QWORD */
if (input->disp_size == 32 || input->disp_size == 64)
goto err;
if (s != 1 && i != -1)
goto err; /* no can do, in 16-bit EA */
if (b == -1 && i != -1) {
int tmp = b;
b = i;
i = tmp;
} /* swap */
if ((b == R_SI || b == R_DI) && i != -1) {
int tmp = b;
b = i;
i = tmp;
}
/* have BX/BP as base, SI/DI index */
if (b == i)
goto err; /* shouldn't ever happen, in theory */
if (i != -1 && b != -1 &&
(i == R_BP || i == R_BX || b == R_SI || b == R_DI))
goto err; /* invalid combinations */
if (b == -1) /* pure offset: handled above */
goto err; /* so if it gets to here, panic! */
rm = -1;
if (i != -1)
switch (i * 256 + b) {
case R_SI * 256 + R_BX:
rm = 0;
break;
case R_DI * 256 + R_BX:
rm = 1;
break;
case R_SI * 256 + R_BP:
rm = 2;
break;
case R_DI * 256 + R_BP:
rm = 3;
break;
} else
switch (b) {
case R_SI:
rm = 4;
break;
case R_DI:
rm = 5;
break;
case R_BP:
rm = 6;
break;
case R_BX:
rm = 7;
break;
}
if (rm == -1) /* can't happen, in theory */
goto err; /* so panic if it does */
mod = memory_mod(eaflags, ins, o, known, rm != 6);
output->sib_present = false; /* no SIB - it's 16-bit */
output->bytes = mod; /* bytes of offset needed */
output->modrm = GEN_MODRM(mod, rfield, rm);
}
if (eaflags & EAF_REL) {
/* Explicit REL reference with indirect memory */
nasm_warn(WARN_OTHER,
"indirect address displacements cannot be RIP-relative");
}
}
}
output->size = 1 + output->sib_present + output->bytes;
/*
* The type parsed might not match one supplied by
* a caller. In this case exit with error and let
* the caller to decide how critical it is.
*/
if (output->type != expected)
goto err_set_msg;
return 0;
err:
output->type = EA_INVALID;
goto err_set_msg;
err_set_msg:
if (!errmsg) {
/* Default error message */
static char invalid_address_msg[40];
snprintf(invalid_address_msg, sizeof invalid_address_msg,
"invalid %d-bit effective address", addrbits);
errmsg = invalid_address_msg;
}
*errmsgp = errmsg;
return -1;
}
static void add_asp(insn *ins)
{
const int bits = ins->bits;
int j, valid;
int defdisp;
int asp_bits;
valid = (bits == 64) ? 64|32 : 32|16;
switch (ins->prefixes[PPS_ASIZE]) {
case P_A16:
valid &= 16;
break;
case P_A32:
valid &= 32;
break;
case P_A64:
valid &= 64;
break;
case P_ASP:
valid &= (bits == 32) ? 16 : 32;
break;
default:
break;
}
for (j = 0; j < ins->operands; j++) {
if (is_class(MEMORY, ins->oprs[j].type)) {
opflags_t i, b;
/* Verify as Register */
if (!is_register(ins->oprs[j].indexreg))
i = 0;
else
i = nasm_reg_flags[ins->oprs[j].indexreg];
/* Verify as Register */
if (!is_register(ins->oprs[j].basereg))
b = 0;
else
b = nasm_reg_flags[ins->oprs[j].basereg];
if (ins->oprs[j].scale == 0)
i = 0;
if (!i && !b) {
int ds = ins->oprs[j].disp_size;
if ((bits != 64 && ds > 8) ||
(bits == 64 && ds == 16))
valid &= ds;
} else {
if (!(REG16 & ~b))
valid &= 16;
if (!(REG32 & ~b))
valid &= 32;
if (!(REG64 & ~b))
valid &= 64;
if (!(REG16 & ~i))
valid &= 16;
if (!(REG32 & ~i))
valid &= 32;
if (!(REG64 & ~i))
valid &= 64;
}
}
}
asp_bits = (bits == 32) ? 16 : 32;
if (valid & bits) {
ins->addr_size = bits;
} else if (valid & asp_bits) {
/* Add an address size prefix */
ins->prefixes[PPS_ASIZE] = (bits == 32) ? P_A16 : P_A32;
ins->addr_size = asp_bits;
} else {
/* Impossible... */
nasm_nonfatal("impossible combination of address sizes");
ins->addr_size = bits; /* Error recovery */
}
defdisp = ins->addr_size == 16 ? 16 : 32;
for (j = 0; j < ins->operands; j++) {
int disp_size;
if (!is_class(MEM_OFFS, ins->oprs[j].type))
continue;
disp_size = ins->oprs[j].disp_size;
if (!disp_size)
disp_size = defdisp;
if (disp_size != ins->addr_size) {
/*
* mem_offs sizes must match the address size; if not,
* strip the MEM_OFFS bit and match only EA instructions
*/
ins->oprs[j].type &= ~(MEM_OFFS & ~MEMORY);
}
}
}
/*
* Set the initial operand size, based only on the mode and any prefixes.
* The actual operand size may change after instruction pattern selection.
*/
static void set_initial_opsize(insn *ins)
{
const int bits = ins->bits;
switch (ins->prefixes[PPS_OSIZE]) {
case P_OSP:
ins->op_size = bits == 16 ? 32 : 16;
break;
case P_O16:
ins->op_size = 16;
break;
case P_O32:
ins->op_size = 32;
break;
case P_O64:
ins->op_size = 64;
break;
default:
ins->op_size = bits == 16 ? 16 : 32;
break;
}
}
static void insn_early_setup(insn *instruction)
{
/* Check to see if we need an address-size prefix */
add_asp(instruction);
/* Set default/prefix-controlled operand size */
set_initial_opsize(instruction);
}
static void do_list_nonfinal_pass(int64_t start)
{
struct out_data dummy;
nasm_zero(dummy);
/* OUT_RAWDATA with .data is special */
dummy.type = OUT_RAWDATA;
dummy.loc = location;
dummy.size = location.offset - start;
dummy.loc.offset = start;
lfmt->output(&dummy);
}
static inline void list_nonfinal_pass(int64_t start)
{
if (list_option('p'))
do_list_nonfinal_pass(start);
}
/*
* Process a single instruction without TIMES; this is a common case
* so optimize it.
*/
static void process_one_insn(insn *ins)
{
int64_t l;
ins->loc = location;
if (!pass_final()) {
l = insn_size(ins);
if (l < 0)
return; /* Invalid instruction */
list_nonfinal_pass(ins->loc.offset);
} else {
l = assemble(ins);
nasm_assert(l >= 0); /* Invalid instruction here: bad */
}
increment_offset(l);
}
/*
* Process an instruction with TIMES handling. As this instruction
* may end up getting processed multiple times and it is permitted
* for the intervening layers to change the contents of the instruction
* structure, it is necessary to keep the original and re-initializing
* the structure on each iteration.
*/
static void process_times_insn(insn *ins)
{
int64_t l;
insn tmpins;
ins->loc = location;
/*
* NOTE: insn_size() and therefore assemble() can change
* ins->times (usually to 1) when called due to merging certain
* operations (e.g. TIMES x RESB y).
*
* Therefore, it is necessary to check the returned times
* value for each iteration.
*/
if (!pass_final()) {
int64_t times = ins->times;
while (times > 0) {
tmpins = *ins;
tmpins.loc.offset = location.offset;
tmpins.times = times;
l = insn_size(&tmpins);
if (l < 0)
break; /* Invalid instruction */
increment_offset(l);
times = tmpins.times - 1;
}
list_nonfinal_pass(ins->loc.offset); /* The original starting offset */
} else {
int64_t times;
tmpins = *ins;
l = assemble(&tmpins);
nasm_assert(l >= 0); /* Invalid instruction here: bad */
increment_offset(l);
times = tmpins.times;
if (times <= 1)
return;
lfmt->uplevel(LIST_TIMES, times);
times--;
while (times) {
tmpins = *ins;
tmpins.loc.offset = location.offset;
tmpins.times = times;
l = assemble(&tmpins);
nasm_assert(l >= 0);
increment_offset(l);
times = tmpins.times - 1;
}
lfmt->downlevel(LIST_TIMES);
}
}
/*
* This is the main entry point to this module, called from asm/nasm.c.
*/
void process_insn(insn *ins)
{
if (likely(ins->times == 1)) {
process_one_insn(ins);
} else if (!ins->times) {
/* TIMES 0 = nothing to do */
} else if (likely(ins->times > 0)) {
process_times_insn(ins);
} else {
nasm_nonfatalf(ERR_PASS2, "TIMES value %"PRId32" is negative",
ins->times);
}
}