blob: 312a507d48fa49ff2cebbe8e68fdc5303c1a9eea [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>
/*@unused@*/ RCSID("$Id: x86bc.c,v 1.1.1.1 2012/03/29 17:21:00 uid42307 Exp $");
#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,
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,
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, 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,
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,
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,
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 *drex,
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) {
if (drex) {
*drex |= ((reg & 8) >> 3) << rexbit;
} else {
/* 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, unsigned int drex,
unsigned int need_drex, 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 */
x86_ea->drex = (unsigned char)drex;
x86_ea->need_drex = (unsigned char)need_drex;
}
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;
x86_ea->need_drex = 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->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;
x86_ea->drex = 0;
x86_ea->need_drex = 0;
return x86_ea;
}
x86_effaddr *
yasm_x86__ea_create_reg(x86_effaddr *x86_ea, unsigned long reg,
unsigned char *rex, unsigned char *drex,
unsigned int bits)
{
unsigned char rm;
if (yasm_x86__set_rex_from_reg(rex, drex, &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.
* 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;
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)) {
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, "%*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 BITS=%u\n",
indent_level, "",
(unsigned int)common->addrsize,
(unsigned int)common->opersize,
(unsigned int)common->lockrep_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++;
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->need_drex ? 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 prefixes never have REX. 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)
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);
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, 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;
unsigned char *bufp_orig = *bufp;
/* 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) {
/* 3-byte VEX; 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->need_drex)
YASM_WRITE_8(*bufp, x86_ea->drex);
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-bufp_orig), 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-bufp_orig),
bc, 1, d))
return 1;
*bufp += imm_len;
}
return 0;
}
static int
x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp, 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;
unsigned char *bufp_orig = *bufp;
/*@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-bufp_orig), 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-bufp_orig), 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, 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 *bufp_orig = *bufp;
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-bufp_orig), bc, 1, d))
return 1;
*bufp += i;
jmpfar->segment.size = 16;
if (output_value(&jmpfar->segment, *bufp, 2,
(unsigned long)(*bufp-bufp_orig), 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;
}