blob: a6681552dda095ff0fb38f9f716d71b455865c88 [file] [log] [blame]
/*
* x86 bytecode utility functions
*
* Copyright (C) 2001-2007 Peter Johnson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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 AUTHOR AND OTHER 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 AUTHOR OR OTHER CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <util.h>
#include <libyasm.h>
#include "x86arch.h"
/* Bytecode callback function prototypes */
static void x86_bc_insn_destroy(void *contents);
static void x86_bc_insn_print(const void *contents, FILE *f,
int indent_level);
static int x86_bc_insn_calc_len(yasm_bytecode *bc,
yasm_bc_add_span_func add_span,
void *add_span_data);
static int x86_bc_insn_expand(yasm_bytecode *bc, int span, long old_val,
long new_val, /*@out@*/ long *neg_thres,
/*@out@*/ long *pos_thres);
static int x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp,
unsigned char *bufstart,
void *d, yasm_output_value_func output_value,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void x86_bc_jmp_destroy(void *contents);
static void x86_bc_jmp_print(const void *contents, FILE *f, int indent_level);
static int x86_bc_jmp_calc_len(yasm_bytecode *bc,
yasm_bc_add_span_func add_span,
void *add_span_data);
static int x86_bc_jmp_expand(yasm_bytecode *bc, int span, long old_val,
long new_val, /*@out@*/ long *neg_thres,
/*@out@*/ long *pos_thres);
static int x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp,
unsigned char *bufstart,
void *d, yasm_output_value_func output_value,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void x86_bc_jmpfar_destroy(void *contents);
static void x86_bc_jmpfar_print(const void *contents, FILE *f,
int indent_level);
static int x86_bc_jmpfar_calc_len(yasm_bytecode *bc,
yasm_bc_add_span_func add_span,
void *add_span_data);
static int x86_bc_jmpfar_tobytes
(yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
/*@null@*/ yasm_output_reloc_func output_reloc);
/* Bytecode callback structures */
static const yasm_bytecode_callback x86_bc_callback_insn = {
x86_bc_insn_destroy,
x86_bc_insn_print,
yasm_bc_finalize_common,
NULL,
x86_bc_insn_calc_len,
x86_bc_insn_expand,
x86_bc_insn_tobytes,
0
};
static const yasm_bytecode_callback x86_bc_callback_jmp = {
x86_bc_jmp_destroy,
x86_bc_jmp_print,
yasm_bc_finalize_common,
NULL,
x86_bc_jmp_calc_len,
x86_bc_jmp_expand,
x86_bc_jmp_tobytes,
0
};
static const yasm_bytecode_callback x86_bc_callback_jmpfar = {
x86_bc_jmpfar_destroy,
x86_bc_jmpfar_print,
yasm_bc_finalize_common,
NULL,
x86_bc_jmpfar_calc_len,
yasm_bc_expand_common,
x86_bc_jmpfar_tobytes,
0
};
int
yasm_x86__set_rex_from_reg(unsigned char *rex, unsigned char *low3,
uintptr_t reg, unsigned int bits,
x86_rex_bit_pos rexbit)
{
*low3 = (unsigned char)(reg&7);
if (bits == 64) {
x86_expritem_reg_size size = (x86_expritem_reg_size)(reg & ~0xFUL);
if (size == X86_REG8X || (reg & 0xF) >= 8) {
/* Check to make sure we can set it */
if (*rex == 0xff) {
yasm_error_set(YASM_ERROR_TYPE,
N_("cannot use A/B/C/DH with instruction needing REX"));
return 1;
}
*rex |= 0x40 | (((reg & 8) >> 3) << rexbit);
} else if (size == X86_REG8 && (reg & 7) >= 4) {
/* AH/BH/CH/DH, so no REX allowed */
if (*rex != 0 && *rex != 0xff) {
yasm_error_set(YASM_ERROR_TYPE,
N_("cannot use A/B/C/DH with instruction needing REX"));
return 1;
}
*rex = 0xff; /* Flag so we can NEVER set it (see above) */
}
}
return 0;
}
void
yasm_x86__bc_transform_insn(yasm_bytecode *bc, x86_insn *insn)
{
yasm_bc_transform(bc, &x86_bc_callback_insn, insn);
}
void
yasm_x86__bc_transform_jmp(yasm_bytecode *bc, x86_jmp *jmp)
{
yasm_bc_transform(bc, &x86_bc_callback_jmp, jmp);
}
void
yasm_x86__bc_transform_jmpfar(yasm_bytecode *bc, x86_jmpfar *jmpfar)
{
yasm_bc_transform(bc, &x86_bc_callback_jmpfar, jmpfar);
}
void
yasm_x86__ea_init(x86_effaddr *x86_ea, unsigned int spare,
yasm_bytecode *precbc)
{
if (yasm_value_finalize(&x86_ea->ea.disp, precbc))
yasm_error_set(YASM_ERROR_TOO_COMPLEX,
N_("effective address too complex"));
x86_ea->modrm &= 0xC7; /* zero spare/reg bits */
x86_ea->modrm |= (spare << 3) & 0x38; /* plug in provided bits */
}
void
yasm_x86__ea_set_disponly(x86_effaddr *x86_ea)
{
x86_ea->valid_modrm = 0;
x86_ea->need_modrm = 0;
x86_ea->valid_sib = 0;
x86_ea->need_sib = 0;
}
static x86_effaddr *
ea_create(void)
{
x86_effaddr *x86_ea = yasm_xmalloc(sizeof(x86_effaddr));
yasm_value_initialize(&x86_ea->ea.disp, NULL, 0);
x86_ea->ea.need_nonzero_len = 0;
x86_ea->ea.need_disp = 0;
x86_ea->ea.nosplit = 0;
x86_ea->ea.strong = 0;
x86_ea->ea.segreg = 0;
x86_ea->ea.pc_rel = 0;
x86_ea->ea.not_pc_rel = 0;
x86_ea->ea.data_len = 0;
x86_ea->vsib_mode = 0;
x86_ea->modrm = 0;
x86_ea->valid_modrm = 0;
x86_ea->need_modrm = 0;
x86_ea->sib = 0;
x86_ea->valid_sib = 0;
x86_ea->need_sib = 0;
return x86_ea;
}
x86_effaddr *
yasm_x86__ea_create_reg(x86_effaddr *x86_ea, unsigned long reg,
unsigned char *rex, unsigned int bits)
{
unsigned char rm;
if (yasm_x86__set_rex_from_reg(rex, &rm, reg, bits, X86_REX_B))
return NULL;
if (!x86_ea)
x86_ea = ea_create();
x86_ea->modrm = 0xC0 | rm; /* Mod=11, R/M=Reg, Reg=0 */
x86_ea->valid_modrm = 1;
x86_ea->need_modrm = 1;
return x86_ea;
}
yasm_effaddr *
yasm_x86__ea_create_expr(yasm_arch *arch, yasm_expr *e)
{
yasm_arch_x86 *arch_x86 = (yasm_arch_x86 *)arch;
x86_effaddr *x86_ea;
x86_ea = ea_create();
if (arch_x86->parser == X86_PARSER_GAS) {
/* Need to change foo+rip into foo wrt rip (even in .intel_syntax mode).
* Note this assumes a particular ordering coming from the parser
* to work (it's not very smart)!
*/
if (e->op == YASM_EXPR_ADD && e->terms[0].type == YASM_EXPR_REG
&& e->terms[0].data.reg == X86_RIP) {
/* replace register with 0 */
e->terms[0].type = YASM_EXPR_INT;
e->terms[0].data.intn = yasm_intnum_create_uint(0);
/* build new wrt expression */
e = yasm_expr_create(YASM_EXPR_WRT, yasm_expr_expr(e),
yasm_expr_reg(X86_RIP), e->line);
}
}
yasm_value_initialize(&x86_ea->ea.disp, e, 0);
x86_ea->ea.need_disp = 1;
x86_ea->need_modrm = 1;
/* We won't know whether we need an SIB until we know more about expr and
* the BITS/address override setting.
*/
x86_ea->need_sib = 0xff;
x86_ea->ea.data_len = 0;
return (yasm_effaddr *)x86_ea;
}
/*@-compmempass@*/
x86_effaddr *
yasm_x86__ea_create_imm(x86_effaddr *x86_ea, yasm_expr *imm,
unsigned int im_len)
{
if (!x86_ea)
x86_ea = ea_create();
yasm_value_initialize(&x86_ea->ea.disp, imm, im_len);
x86_ea->ea.need_disp = 1;
return x86_ea;
}
/*@=compmempass@*/
void
yasm_x86__bc_apply_prefixes(x86_common *common, unsigned char *rex,
unsigned int def_opersize_64,
unsigned int num_prefixes, uintptr_t *prefixes)
{
unsigned int i;
int first = 1;
for (i=0; i<num_prefixes; i++) {
switch ((x86_parse_insn_prefix)(prefixes[i] & 0xff00)) {
/*To be accurate, we should enforce that TSX hints come only with a
predefined set of instructions, and in most cases only with F0
prefix. Otherwise they will have completely different semantics.
But F0 prefix can come only with a predefined set of instructions
too. And if it comes with other instructions, CPU will #UD.
Hence, F0-applicability should be enforced too. But it's not
currently. Maybe it is the decision made, that user should know
himself what he is doing with LOCK prefix. In this case, we should
not enforce TSX hints applicability too. And let user take care of
correct usage of TSX hints.
That is what we are going to do.*/
case X86_ACQREL:
if (common->acqrel_pre != 0)
yasm_warn_set(YASM_WARN_GENERAL,
N_("multiple XACQUIRE/XRELEASE prefixes, "
"using leftmost"));
common->acqrel_pre = (unsigned char)prefixes[i] & 0xff;
break;
case X86_LOCKREP:
if (common->lockrep_pre != 0)
yasm_warn_set(YASM_WARN_GENERAL,
N_("multiple LOCK or REP prefixes, using leftmost"));
common->lockrep_pre = (unsigned char)prefixes[i] & 0xff;
break;
case X86_ADDRSIZE:
common->addrsize = (unsigned char)prefixes[i] & 0xff;
break;
case X86_OPERSIZE:
common->opersize = (unsigned char)prefixes[i] & 0xff;
if (common->mode_bits == 64 && common->opersize == 64 &&
def_opersize_64 != 64) {
if (!rex)
yasm_warn_set(YASM_WARN_GENERAL,
N_("ignoring REX prefix on jump"));
else if (*rex == 0xff)
yasm_warn_set(YASM_WARN_GENERAL,
N_("REX prefix not allowed on this instruction, ignoring"));
else
*rex = 0x48;
}
break;
case X86_SEGREG:
/* This is a hack.. we should really be putting this in the
* the effective address!
*/
common->lockrep_pre = (unsigned char)prefixes[i] & 0xff;
break;
case X86_REX:
if (!rex)
yasm_warn_set(YASM_WARN_GENERAL,
N_("ignoring REX prefix on jump"));
else if (*rex == 0xff)
yasm_warn_set(YASM_WARN_GENERAL,
N_("REX prefix not allowed on this instruction, ignoring"));
else {
if (*rex != 0) {
if (first)
yasm_warn_set(YASM_WARN_GENERAL,
N_("overriding generated REX prefix"));
else
yasm_warn_set(YASM_WARN_GENERAL,
N_("multiple REX prefixes, using leftmost"));
}
/* Here we assume that we can't get this prefix in non
* 64 bit mode due to checks in parse_check_prefix().
*/
common->mode_bits = 64;
*rex = (unsigned char)prefixes[i] & 0xff;
}
first = 0;
break;
}
}
}
static void
x86_bc_insn_destroy(void *contents)
{
x86_insn *insn = (x86_insn *)contents;
if (insn->x86_ea)
yasm_x86__ea_destroy((yasm_effaddr *)insn->x86_ea);
if (insn->imm) {
yasm_value_delete(insn->imm);
yasm_xfree(insn->imm);
}
yasm_xfree(contents);
}
static void
x86_bc_jmp_destroy(void *contents)
{
x86_jmp *jmp = (x86_jmp *)contents;
yasm_value_delete(&jmp->target);
yasm_xfree(contents);
}
static void
x86_bc_jmpfar_destroy(void *contents)
{
x86_jmpfar *jmpfar = (x86_jmpfar *)contents;
yasm_value_delete(&jmpfar->segment);
yasm_value_delete(&jmpfar->offset);
yasm_xfree(contents);
}
void
yasm_x86__ea_destroy(yasm_effaddr *ea)
{
yasm_value_delete(&ea->disp);
yasm_xfree(ea);
}
void
yasm_x86__ea_print(const yasm_effaddr *ea, FILE *f, int indent_level)
{
const x86_effaddr *x86_ea = (const x86_effaddr *)ea;
fprintf(f, "%*sDisp:\n", indent_level, "");
yasm_value_print(&ea->disp, f, indent_level+1);
fprintf(f, "%*sNoSplit=%u\n", indent_level, "", (unsigned int)ea->nosplit);
fprintf(f, "%*sSegmentOv=%02x\n", indent_level, "",
(unsigned int)x86_ea->ea.segreg);
fprintf(f, "%*sVSIBMode=%u\n", indent_level, "",
(unsigned int)x86_ea->vsib_mode);
fprintf(f, "%*sModRM=%03o ValidRM=%u NeedRM=%u\n", indent_level, "",
(unsigned int)x86_ea->modrm, (unsigned int)x86_ea->valid_modrm,
(unsigned int)x86_ea->need_modrm);
fprintf(f, "%*sSIB=%03o ValidSIB=%u NeedSIB=%u\n", indent_level, "",
(unsigned int)x86_ea->sib, (unsigned int)x86_ea->valid_sib,
(unsigned int)x86_ea->need_sib);
}
static void
x86_common_print(const x86_common *common, FILE *f, int indent_level)
{
fprintf(f, "%*sAddrSize=%u OperSize=%u LockRepPre=%02x "
"ACQREL_Pre=%02x BITS=%u\n",
indent_level, "",
(unsigned int)common->addrsize,
(unsigned int)common->opersize,
(unsigned int)common->lockrep_pre,
(unsigned int)common->acqrel_pre,
(unsigned int)common->mode_bits);
}
static void
x86_opcode_print(const x86_opcode *opcode, FILE *f, int indent_level)
{
fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n", indent_level, "",
(unsigned int)opcode->opcode[0],
(unsigned int)opcode->opcode[1],
(unsigned int)opcode->opcode[2],
(unsigned int)opcode->len);
}
static void
x86_bc_insn_print(const void *contents, FILE *f, int indent_level)
{
const x86_insn *insn = (const x86_insn *)contents;
fprintf(f, "%*s_Instruction_\n", indent_level, "");
fprintf(f, "%*sEffective Address:", indent_level, "");
if (insn->x86_ea) {
fprintf(f, "\n");
yasm_x86__ea_print((yasm_effaddr *)insn->x86_ea, f, indent_level+1);
} else
fprintf(f, " (nil)\n");
fprintf(f, "%*sImmediate Value:", indent_level, "");
if (!insn->imm)
fprintf(f, " (nil)\n");
else {
indent_level++;
fprintf(f, "\n");
yasm_value_print(insn->imm, f, indent_level);
indent_level--;
}
x86_opcode_print(&insn->opcode, f, indent_level);
x86_common_print(&insn->common, f, indent_level);
fprintf(f, "%*sSpPre=%02x REX=%03o PostOp=%u\n", indent_level, "",
(unsigned int)insn->special_prefix,
(unsigned int)insn->rex,
(unsigned int)insn->postop);
}
static void
x86_bc_jmp_print(const void *contents, FILE *f, int indent_level)
{
const x86_jmp *jmp = (const x86_jmp *)contents;
fprintf(f, "%*s_Jump_\n", indent_level, "");
fprintf(f, "%*sTarget:\n", indent_level, "");
yasm_value_print(&jmp->target, f, indent_level+1);
/* FIXME
fprintf(f, "%*sOrigin=\n", indent_level, "");
yasm_symrec_print(jmp->origin, f, indent_level+1);
*/
fprintf(f, "\n%*sShort Form:\n", indent_level, "");
if (jmp->shortop.len == 0)
fprintf(f, "%*sNone\n", indent_level+1, "");
else
x86_opcode_print(&jmp->shortop, f, indent_level+1);
fprintf(f, "%*sNear Form:\n", indent_level, "");
if (jmp->nearop.len == 0)
fprintf(f, "%*sNone\n", indent_level+1, "");
else
x86_opcode_print(&jmp->nearop, f, indent_level+1);
fprintf(f, "%*sOpSel=", indent_level, "");
switch (jmp->op_sel) {
case JMP_NONE:
fprintf(f, "None");
break;
case JMP_SHORT:
fprintf(f, "Short");
break;
case JMP_NEAR:
fprintf(f, "Near");
break;
case JMP_SHORT_FORCED:
fprintf(f, "Forced Short");
break;
case JMP_NEAR_FORCED:
fprintf(f, "Forced Near");
break;
default:
fprintf(f, "UNKNOWN!!");
break;
}
x86_common_print(&jmp->common, f, indent_level);
}
static void
x86_bc_jmpfar_print(const void *contents, FILE *f, int indent_level)
{
const x86_jmpfar *jmpfar = (const x86_jmpfar *)contents;
fprintf(f, "%*s_Far_Jump_\n", indent_level, "");
fprintf(f, "%*sSegment:\n", indent_level, "");
yasm_value_print(&jmpfar->segment, f, indent_level+1);
fprintf(f, "%*sOffset:\n", indent_level, "");
yasm_value_print(&jmpfar->offset, f, indent_level+1);
x86_opcode_print(&jmpfar->opcode, f, indent_level);
x86_common_print(&jmpfar->common, f, indent_level);
}
static unsigned int
x86_common_calc_len(const x86_common *common)
{
unsigned int len = 0;
if (common->addrsize != 0 && common->addrsize != common->mode_bits)
len++;
if (common->opersize != 0 &&
((common->mode_bits != 64 && common->opersize != common->mode_bits) ||
(common->mode_bits == 64 && common->opersize == 16)))
len++;
if (common->lockrep_pre != 0)
len++;
if (common->acqrel_pre != 0)
len++;
return len;
}
static int
x86_bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
void *add_span_data)
{
x86_insn *insn = (x86_insn *)bc->contents;
x86_effaddr *x86_ea = insn->x86_ea;
yasm_value *imm = insn->imm;
if (x86_ea) {
/* Check validity of effective address and calc R/M bits of
* Mod/RM byte and SIB byte. We won't know the Mod field
* of the Mod/RM byte until we know more about the
* displacement.
*/
if (yasm_x86__expr_checkea(x86_ea, &insn->common.addrsize,
insn->common.mode_bits, insn->postop == X86_POSTOP_ADDRESS16,
&insn->rex, bc))
/* failed, don't bother checking rest of insn */
return -1;
if (x86_ea->ea.disp.size == 0 && x86_ea->ea.need_nonzero_len) {
/* Handle unknown case, default to byte-sized and set as
* critical expression.
*/
x86_ea->ea.disp.size = 8;
add_span(add_span_data, bc, 1, &x86_ea->ea.disp, -128, 127);
}
bc->len += x86_ea->ea.disp.size/8;
/* Handle address16 postop case */
if (insn->postop == X86_POSTOP_ADDRESS16)
insn->common.addrsize = 0;
/* Compute length of ea and add to total */
bc->len += x86_ea->need_modrm + (x86_ea->need_sib ? 1:0);
bc->len += (x86_ea->ea.segreg != 0) ? 1 : 0;
}
if (imm) {
unsigned int immlen = imm->size;
/* TODO: check imm->len vs. sized len from expr? */
/* Handle signext_imm8 postop special-casing */
if (insn->postop == X86_POSTOP_SIGNEXT_IMM8) {
/*@null@*/ /*@only@*/ yasm_intnum *num;
num = yasm_value_get_intnum(imm, NULL, 0);
if (!num) {
/* Unknown; default to byte form and set as critical
* expression.
*/
immlen = 8;
add_span(add_span_data, bc, 2, imm, -128, 127);
} else {
if (yasm_intnum_in_range(num, -128, 127)) {
/* We can use the sign-extended byte form: shorten
* the immediate length to 1 and make the byte form
* permanent.
*/
imm->size = 8;
imm->sign = 1;
immlen = 8;
} else {
/* We can't. Copy over the word-sized opcode. */
insn->opcode.opcode[0] =
insn->opcode.opcode[insn->opcode.len];
insn->opcode.len = 1;
}
insn->postop = X86_POSTOP_NONE;
yasm_intnum_destroy(num);
}
}
bc->len += immlen/8;
}
/* VEX and XOP prefixes never have REX (it's embedded in the opcode).
* For VEX, we can come into this function with the three byte form,
* so we need to see if we can optimize to the two byte form.
* We can't do it earlier, as we don't know all of the REX byte until now.
*/
if (insn->special_prefix == 0xC4) {
/* See if we can shorten the VEX prefix to its two byte form.
* In order to do this, REX.X, REX.B, and REX.W/VEX.W must all be 0,
* and the VEX mmmmm field must be 1.
*/
if ((insn->opcode.opcode[0] & 0x1F) == 1 &&
(insn->opcode.opcode[1] & 0x80) == 0 &&
(insn->rex == 0xff || (insn->rex & 0x0B) == 0)) {
insn->opcode.opcode[0] = insn->opcode.opcode[1];
insn->opcode.opcode[1] = insn->opcode.opcode[2];
insn->opcode.opcode[2] = 0; /* sanity */
insn->opcode.len = 2;
insn->special_prefix = 0xC5; /* mark as two-byte VEX */
}
} else if (insn->rex != 0xff && insn->rex != 0 &&
insn->special_prefix != 0xC5 && insn->special_prefix != 0x8F)
bc->len++;
bc->len += insn->opcode.len;
bc->len += x86_common_calc_len(&insn->common);
bc->len += (insn->special_prefix != 0) ? 1:0;
return 0;
}
static int
x86_bc_insn_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
/*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
{
x86_insn *insn = (x86_insn *)bc->contents;
x86_effaddr *x86_ea = insn->x86_ea;
yasm_effaddr *ea = &x86_ea->ea;
yasm_value *imm = insn->imm;
if (ea && span == 1) {
/* Change displacement length into word-sized */
if (ea->disp.size == 8) {
ea->disp.size = (insn->common.addrsize == 16) ? 16 : 32;
x86_ea->modrm &= ~0300;
x86_ea->modrm |= 0200;
bc->len--;
bc->len += ea->disp.size/8;
}
}
if (imm && span == 2) {
if (insn->postop == X86_POSTOP_SIGNEXT_IMM8) {
/* Update bc->len for new opcode and immediate size */
bc->len -= insn->opcode.len;
bc->len += imm->size/8;
/* Change to the word-sized opcode */
insn->opcode.opcode[0] = insn->opcode.opcode[insn->opcode.len];
insn->opcode.len = 1;
insn->postop = X86_POSTOP_NONE;
}
}
return 0;
}
static int
x86_bc_jmp_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
void *add_span_data)
{
x86_jmp *jmp = (x86_jmp *)bc->contents;
yasm_bytecode *target_prevbc;
unsigned char opersize;
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmp->common.opersize == 0) ?
jmp->common.mode_bits : jmp->common.opersize;
bc->len += x86_common_calc_len(&jmp->common);
if (jmp->op_sel == JMP_NEAR_FORCED || jmp->shortop.len == 0) {
if (jmp->nearop.len == 0) {
yasm_error_set(YASM_ERROR_TYPE, N_("near jump does not exist"));
return -1;
}
/* Near jump, no spans needed */
if (jmp->shortop.len == 0)
jmp->op_sel = JMP_NEAR;
bc->len += jmp->nearop.len;
bc->len += (opersize == 16) ? 2 : 4;
return 0;
}
if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) {
if (jmp->shortop.len == 0) {
yasm_error_set(YASM_ERROR_TYPE, N_("short jump does not exist"));
return -1;
}
/* We want to be sure to error if we exceed short length, so
* put it in as a dependent expression (falling through).
*/
}
if (jmp->target.rel
&& (!yasm_symrec_get_label(jmp->target.rel, &target_prevbc)
|| target_prevbc->section != bc->section)) {
/* External or out of segment, so we can't check distance.
* Allowing short jumps depends on the objfmt supporting
* 8-bit relocs. While most don't, some might, so allow it here.
* Otherwise default to word-sized.
* The objfmt will error if not supported.
*/
if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) {
if (jmp->op_sel == JMP_NONE)
jmp->op_sel = JMP_SHORT;
bc->len += jmp->shortop.len + 1;
} else {
jmp->op_sel = JMP_NEAR;
bc->len += jmp->nearop.len;
bc->len += (opersize == 16) ? 2 : 4;
}
return 0;
}
/* Default to short jump and generate span */
if (jmp->op_sel == JMP_NONE)
jmp->op_sel = JMP_SHORT;
bc->len += jmp->shortop.len + 1;
add_span(add_span_data, bc, 1, &jmp->target, -128+(long)bc->len,
127+(long)bc->len);
return 0;
}
static int
x86_bc_jmp_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
/*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
{
x86_jmp *jmp = (x86_jmp *)bc->contents;
unsigned char opersize;
if (span != 1)
yasm_internal_error(N_("unrecognized span id"));
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmp->common.opersize == 0) ?
jmp->common.mode_bits : jmp->common.opersize;
if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) {
yasm_error_set(YASM_ERROR_VALUE, N_("short jump out of range"));
return -1;
}
if (jmp->op_sel == JMP_NEAR)
yasm_internal_error(N_("trying to expand an already-near jump"));
/* Upgrade to a near jump */
jmp->op_sel = JMP_NEAR;
bc->len -= jmp->shortop.len + 1;
bc->len += jmp->nearop.len;
bc->len += (opersize == 16) ? 2 : 4;
return 0;
}
static int
x86_bc_jmpfar_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
void *add_span_data)
{
x86_jmpfar *jmpfar = (x86_jmpfar *)bc->contents;
unsigned char opersize;
opersize = (jmpfar->common.opersize == 0) ?
jmpfar->common.mode_bits : jmpfar->common.opersize;
bc->len += jmpfar->opcode.len;
bc->len += 2; /* segment */
bc->len += (opersize == 16) ? 2 : 4;
bc->len += x86_common_calc_len(&jmpfar->common);
return 0;
}
static void
x86_common_tobytes(const x86_common *common, unsigned char **bufp,
unsigned int segreg)
{
if (segreg != 0)
YASM_WRITE_8(*bufp, (unsigned char)segreg);
if (common->addrsize != 0 && common->addrsize != common->mode_bits)
YASM_WRITE_8(*bufp, 0x67);
if (common->opersize != 0 &&
((common->mode_bits != 64 && common->opersize != common->mode_bits) ||
(common->mode_bits == 64 && common->opersize == 16)))
YASM_WRITE_8(*bufp, 0x66);
/*TSX hints come before lock prefix*/
if (common->acqrel_pre != 0)
YASM_WRITE_8(*bufp, common->acqrel_pre);
if (common->lockrep_pre != 0)
YASM_WRITE_8(*bufp, common->lockrep_pre);
}
static void
x86_opcode_tobytes(const x86_opcode *opcode, unsigned char **bufp)
{
unsigned int i;
for (i=0; i<opcode->len; i++)
YASM_WRITE_8(*bufp, opcode->opcode[i]);
}
static int
x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp,
unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
/*@unused@*/ yasm_output_reloc_func output_reloc)
{
x86_insn *insn = (x86_insn *)bc->contents;
/*@null@*/ x86_effaddr *x86_ea = (x86_effaddr *)insn->x86_ea;
yasm_value *imm = insn->imm;
/* Prefixes */
x86_common_tobytes(&insn->common, bufp,
x86_ea ? (unsigned int)(x86_ea->ea.segreg>>8) : 0);
if (insn->special_prefix != 0)
YASM_WRITE_8(*bufp, insn->special_prefix);
if (insn->special_prefix == 0xC4 || insn->special_prefix == 0x8F) {
/* 3-byte VEX/XOP; merge in 1s complement of REX.R, REX.X, REX.B */
insn->opcode.opcode[0] &= 0x1F;
if (insn->rex != 0xff)
insn->opcode.opcode[0] |= ((~insn->rex) & 0x07) << 5;
/* merge REX.W via ORing; there should never be a case in which REX.W
* is important when VEX.W is already set by the instruction.
*/
if (insn->rex != 0xff && (insn->rex & 0x8) != 0)
insn->opcode.opcode[1] |= 0x80;
} else if (insn->special_prefix == 0xC5) {
/* 2-byte VEX; merge in 1s complement of REX.R */
insn->opcode.opcode[0] &= 0x7F;
if (insn->rex != 0xff && (insn->rex & 0x4) == 0)
insn->opcode.opcode[0] |= 0x80;
/* No other REX bits should be set */
if (insn->rex != 0xff && (insn->rex & 0xB) != 0)
yasm_internal_error(N_("x86: REX.WXB set, but 2-byte VEX"));
} else if (insn->rex != 0xff && insn->rex != 0) {
if (insn->common.mode_bits != 64)
yasm_internal_error(N_("x86: got a REX prefix in non-64-bit mode"));
YASM_WRITE_8(*bufp, insn->rex);
}
/* Opcode */
x86_opcode_tobytes(&insn->opcode, bufp);
/* Effective address: ModR/M (if required), SIB (if required), and
* displacement (if required).
*/
if (x86_ea) {
if (x86_ea->need_modrm) {
if (!x86_ea->valid_modrm)
yasm_internal_error(N_("invalid Mod/RM in x86 tobytes_insn"));
YASM_WRITE_8(*bufp, x86_ea->modrm);
}
if (x86_ea->need_sib) {
if (!x86_ea->valid_sib)
yasm_internal_error(N_("invalid SIB in x86 tobytes_insn"));
YASM_WRITE_8(*bufp, x86_ea->sib);
}
if (x86_ea->ea.need_disp) {
unsigned int disp_len = x86_ea->ea.disp.size/8;
if (x86_ea->ea.disp.ip_rel) {
/* Adjust relative displacement to end of bytecode */
/*@only@*/ yasm_intnum *delta;
delta = yasm_intnum_create_int(-(long)bc->len);
if (!x86_ea->ea.disp.abs)
x86_ea->ea.disp.abs =
yasm_expr_create_ident(yasm_expr_int(delta), bc->line);
else
x86_ea->ea.disp.abs =
yasm_expr_create(YASM_EXPR_ADD,
yasm_expr_expr(x86_ea->ea.disp.abs),
yasm_expr_int(delta), bc->line);
}
if (output_value(&x86_ea->ea.disp, *bufp, disp_len,
(unsigned long)(*bufp-bufstart), bc, 1, d))
return 1;
*bufp += disp_len;
}
}
/* Immediate (if required) */
if (imm) {
unsigned int imm_len;
if (insn->postop == X86_POSTOP_SIGNEXT_IMM8) {
/* If we got here with this postop still set, we need to force
* imm size to 8 here.
*/
imm->size = 8;
imm->sign = 1;
imm_len = 1;
} else
imm_len = imm->size/8;
if (output_value(imm, *bufp, imm_len, (unsigned long)(*bufp-bufstart),
bc, 1, d))
return 1;
*bufp += imm_len;
}
return 0;
}
static int
x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp,
unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
/*@unused@*/ yasm_output_reloc_func output_reloc)
{
x86_jmp *jmp = (x86_jmp *)bc->contents;
unsigned char opersize;
unsigned int i;
/*@only@*/ yasm_intnum *delta;
/* Prefixes */
x86_common_tobytes(&jmp->common, bufp, 0);
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmp->common.opersize == 0) ?
jmp->common.mode_bits : jmp->common.opersize;
/* Check here again to see if forms are actually legal. */
switch (jmp->op_sel) {
case JMP_SHORT_FORCED:
case JMP_SHORT:
/* 1 byte relative displacement */
if (jmp->shortop.len == 0)
yasm_internal_error(N_("short jump does not exist"));
/* Opcode */
x86_opcode_tobytes(&jmp->shortop, bufp);
/* Adjust relative displacement to end of bytecode */
delta = yasm_intnum_create_int(-(long)bc->len);
if (!jmp->target.abs)
jmp->target.abs = yasm_expr_create_ident(yasm_expr_int(delta),
bc->line);
else
jmp->target.abs =
yasm_expr_create(YASM_EXPR_ADD,
yasm_expr_expr(jmp->target.abs),
yasm_expr_int(delta), bc->line);
jmp->target.size = 8;
jmp->target.sign = 1;
if (output_value(&jmp->target, *bufp, 1,
(unsigned long)(*bufp-bufstart), bc, 1, d))
return 1;
*bufp += 1;
break;
case JMP_NEAR_FORCED:
case JMP_NEAR:
/* 2/4 byte relative displacement (depending on operand size) */
if (jmp->nearop.len == 0) {
yasm_error_set(YASM_ERROR_TYPE,
N_("near jump does not exist"));
return 1;
}
/* Opcode */
x86_opcode_tobytes(&jmp->nearop, bufp);
i = (opersize == 16) ? 2 : 4;
/* Adjust relative displacement to end of bytecode */
delta = yasm_intnum_create_int(-(long)bc->len);
if (!jmp->target.abs)
jmp->target.abs = yasm_expr_create_ident(yasm_expr_int(delta),
bc->line);
else
jmp->target.abs =
yasm_expr_create(YASM_EXPR_ADD,
yasm_expr_expr(jmp->target.abs),
yasm_expr_int(delta), bc->line);
jmp->target.size = i*8;
jmp->target.sign = 1;
if (output_value(&jmp->target, *bufp, i,
(unsigned long)(*bufp-bufstart), bc, 1, d))
return 1;
*bufp += i;
break;
case JMP_NONE:
yasm_internal_error(N_("jump op_sel cannot be JMP_NONE in tobytes"));
default:
yasm_internal_error(N_("unrecognized relative jump op_sel"));
}
return 0;
}
static int
x86_bc_jmpfar_tobytes(yasm_bytecode *bc, unsigned char **bufp,
unsigned char *bufstart, void *d,
yasm_output_value_func output_value,
/*@unused@*/ yasm_output_reloc_func output_reloc)
{
x86_jmpfar *jmpfar = (x86_jmpfar *)bc->contents;
unsigned int i;
unsigned char opersize;
x86_common_tobytes(&jmpfar->common, bufp, 0);
x86_opcode_tobytes(&jmpfar->opcode, bufp);
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmpfar->common.opersize == 0) ?
jmpfar->common.mode_bits : jmpfar->common.opersize;
/* Absolute displacement: segment and offset */
i = (opersize == 16) ? 2 : 4;
jmpfar->offset.size = i*8;
if (output_value(&jmpfar->offset, *bufp, i,
(unsigned long)(*bufp-bufstart), bc, 1, d))
return 1;
*bufp += i;
jmpfar->segment.size = 16;
if (output_value(&jmpfar->segment, *bufp, 2,
(unsigned long)(*bufp-bufstart), bc, 1, d))
return 1;
*bufp += 2;
return 0;
}
int
yasm_x86__intnum_tobytes(yasm_arch *arch, const yasm_intnum *intn,
unsigned char *buf, size_t destsize, size_t valsize,
int shift, const yasm_bytecode *bc, int warn)
{
/* Write value out. */
yasm_intnum_get_sized(intn, buf, destsize, valsize, shift, 0, warn);
return 0;
}