blob: 4e6962f9d9a590c1906f6b29b3428d48ece58d3a [file] [log] [blame]
/*
* x86 bytecode utility functions
*
* Copyright (C) 2001 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$");
#define YASM_LIB_INTERNAL
#define YASM_BC_INTERNAL
#define YASM_EXPR_INTERNAL
#include <libyasm.h>
#include "x86arch.h"
/* Effective address callback function prototypes */
static void x86_ea_destroy(yasm_effaddr *ea);
static void x86_ea_print(const yasm_effaddr *ea, FILE *f, int indent_level);
/* 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 yasm_bc_resolve_flags x86_bc_insn_resolve
(yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist);
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 yasm_bc_resolve_flags x86_bc_jmp_resolve
(yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist);
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 yasm_bc_resolve_flags x86_bc_jmpfar_resolve
(yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist);
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);
/* Effective address callback structures */
static const yasm_effaddr_callback x86_ea_callback = {
x86_ea_destroy,
x86_ea_print
};
/* 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_resolve,
x86_bc_insn_tobytes
};
static const yasm_bytecode_callback x86_bc_callback_jmp = {
x86_bc_jmp_destroy,
x86_bc_jmp_print,
yasm_bc_finalize_common,
x86_bc_jmp_resolve,
x86_bc_jmp_tobytes
};
static const yasm_bytecode_callback x86_bc_callback_jmpfar = {
x86_bc_jmpfar_destroy,
x86_bc_jmpfar_print,
yasm_bc_finalize_common,
x86_bc_jmpfar_resolve,
x86_bc_jmpfar_tobytes
};
int
yasm_x86__set_rex_from_reg(unsigned char *rex, unsigned char *low3,
unsigned long 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 (*rex == 0xff)
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)
return 1;
*rex = 0xff;
}
}
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 long line)
{
if (yasm_value_finalize(&x86_ea->ea.disp))
yasm__error(line, 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;
}
x86_effaddr *
yasm_x86__ea_create_reg(unsigned long reg, unsigned char *rex,
unsigned int bits)
{
x86_effaddr *x86_ea;
unsigned char rm;
if (yasm_x86__set_rex_from_reg(rex, &rm, reg, bits, X86_REX_B))
return NULL;
x86_ea = yasm_xmalloc(sizeof(x86_effaddr));
x86_ea->ea.callback = &x86_ea_callback;
yasm_value_initialize(&x86_ea->ea.disp, NULL);
x86_ea->ea.disp_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->modrm = 0xC0 | rm; /* Mod=11, R/M=Reg, Reg=0 */
x86_ea->valid_modrm = 1;
x86_ea->need_modrm = 1;
x86_ea->sib = 0;
x86_ea->valid_sib = 0;
x86_ea->need_sib = 0;
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 = yasm_xmalloc(sizeof(x86_effaddr));
x86_ea->ea.callback = &x86_ea_callback;
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);
x86_ea->ea.disp_len = 0;
x86_ea->ea.need_disp = 1;
x86_ea->ea.nosplit = 0;
x86_ea->ea.strong = 0;
x86_ea->ea.segreg = 0;
x86_ea->modrm = 0;
x86_ea->valid_modrm = 0;
x86_ea->need_modrm = 1;
x86_ea->sib = 0;
x86_ea->valid_sib = 0;
/* 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(yasm_expr *imm, unsigned int im_len)
{
x86_effaddr *x86_ea;
x86_ea = yasm_xmalloc(sizeof(x86_effaddr));
x86_ea->ea.callback = &x86_ea_callback;
yasm_value_initialize(&x86_ea->ea.disp, imm);
x86_ea->ea.disp_len = (unsigned char)im_len;
x86_ea->ea.need_disp = 1;
x86_ea->ea.nosplit = 0;
x86_ea->ea.strong = 0;
x86_ea->ea.segreg = 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;
}
/*@=compmempass@*/
void
yasm_x86__bc_apply_prefixes(x86_common *common, unsigned char *rex,
int num_prefixes, unsigned long **prefixes,
unsigned long line)
{
int i;
int first = 1;
for (i=0; i<num_prefixes; i++) {
switch ((x86_parse_insn_prefix)prefixes[i][0]) {
case X86_LOCKREP:
if (common->lockrep_pre != 0)
yasm__warning(YASM_WARN_GENERAL, line,
N_("multiple LOCK or REP prefixes, using leftmost"));
common->lockrep_pre = (unsigned char)prefixes[i][1];
break;
case X86_ADDRSIZE:
common->addrsize = (unsigned char)prefixes[i][1];
break;
case X86_OPERSIZE:
common->opersize = (unsigned char)prefixes[i][1];
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][1];
break;
case X86_REX:
if (!rex)
yasm__warning(YASM_WARN_GENERAL, line,
N_("ignoring REX prefix on jump"));
else if (*rex == 0xff)
yasm__warning(YASM_WARN_GENERAL, line,
N_("REX prefix not allowed on this instruction, ignoring"));
else {
if (*rex != 0) {
if (first)
yasm__warning(YASM_WARN_GENERAL, line,
N_("overriding generated REX prefix"));
else
yasm__warning(YASM_WARN_GENERAL, line,
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][1];
}
first = 0;
break;
}
}
}
static void
x86_bc_insn_destroy(void *contents)
{
x86_insn *insn = (x86_insn *)contents;
if (insn->x86_ea)
yasm_ea_destroy((yasm_effaddr *)insn->x86_ea);
if (insn->imm) {
yasm_value_delete(&insn->imm->val);
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);
}
static void
x86_ea_destroy(yasm_effaddr *ea)
{
}
static void
x86_ea_print(const yasm_effaddr *ea, FILE *f, int indent_level)
{
const x86_effaddr *x86_ea = (const x86_effaddr *)ea;
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_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->val, f, indent_level);
fprintf(f, "%*sLen=%u, Sign=%u\n", indent_level, "",
(unsigned int)insn->imm->len,
(unsigned int)insn->imm->sign);
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_resolve(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 yasm_bc_resolve_flags
x86_bc_insn_resolve(yasm_bytecode *bc, int save,
yasm_calc_bc_dist_func calc_bc_dist)
{
x86_insn *insn = (x86_insn *)bc->contents;
x86_effaddr *x86_ea = insn->x86_ea;
yasm_immval *imm = insn->imm;
yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN;
if (x86_ea) {
/* Create temp copy of disp, etc. */
x86_effaddr eat = *x86_ea; /* structure copy */
/* Don't overwrite original expression portion */
if (x86_ea->ea.disp.abs)
eat.ea.disp.abs = yasm_expr_copy(x86_ea->ea.disp.abs);
/* Handle shortmov special-casing */
if (insn->postop == X86_POSTOP_SHORTMOV &&
insn->common.mode_bits == 64 && insn->common.addrsize == 32 &&
(!eat.ea.disp.abs ||
!yasm_expr__contains(eat.ea.disp.abs, YASM_EXPR_REG))) {
yasm_x86__ea_set_disponly(&eat);
if (save) {
/* Make the short form permanent. */
insn->opcode.opcode[0] = insn->opcode.opcode[1];
}
}
/* 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.
*/
switch (yasm_x86__expr_checkea(&eat, &insn->common.addrsize,
insn->common.mode_bits, insn->postop == X86_POSTOP_ADDRESS16,
&insn->rex, calc_bc_dist, bc->line)) {
case 1:
yasm_expr_destroy(eat.ea.disp.abs);
/* failed, don't bother checking rest of insn */
return YASM_BC_RESOLVE_UNKNOWN_LEN|YASM_BC_RESOLVE_ERROR;
case 2:
yasm_expr_destroy(eat.ea.disp.abs);
/* failed, don't bother checking rest of insn */
return YASM_BC_RESOLVE_UNKNOWN_LEN;
default:
yasm_expr_destroy(eat.ea.disp.abs);
/* okay */
break;
}
if (eat.ea.disp_len != 1) {
/* Fits into a word/dword, or unknown. */
retval = YASM_BC_RESOLVE_NONE; /* may not be smallest size */
/* Handle unknown case, make displen word-sized */
if (eat.ea.disp_len == 0xff)
eat.ea.disp_len = (insn->common.addrsize == 16) ? 2U : 4U;
}
/* Handle address16 postop case */
if (insn->postop == X86_POSTOP_ADDRESS16)
insn->common.addrsize = 0;
/* If we had forced ea->len but had to override, save it now */
if (x86_ea->ea.disp_len != 0 && x86_ea->ea.disp_len != eat.ea.disp_len)
x86_ea->ea.disp_len = eat.ea.disp_len;
if (save) {
eat.ea.disp.abs = x86_ea->ea.disp.abs; /* Copy back original */
*x86_ea = eat; /* structure copy */
if (x86_ea->ea.disp_len == 0) {
yasm_value_delete(&x86_ea->ea.disp);
x86_ea->ea.need_disp = 0;
}
}
/* Compute length of ea and add to total */
bc->len += eat.need_modrm + (eat.need_sib ? 1:0) + eat.ea.disp_len;
bc->len += (eat.ea.segreg != 0) ? 1 : 0;
}
if (imm) {
/*@null@*/ yasm_expr *temp = NULL;
const yasm_intnum *num = NULL;
unsigned int immlen = imm->len;
long val;
if (imm->val.abs) {
temp = yasm_expr_copy(imm->val.abs);
assert(temp != NULL);
num = yasm_expr_get_intnum(&temp, calc_bc_dist);
}
/* TODO: check imm->len vs. sized len from expr? */
switch (insn->postop) {
case X86_POSTOP_SIGNEXT_IMM8:
/* Handle signext_imm8 postop special-casing */
if (imm->val.rel)
val = 1000; /* has relative portion, don't collapse */
else if (num)
val = yasm_intnum_get_int(num);
else
val = 0;
if (val >= -128 && val <= 127) {
/* We can use the sign-extended byte form: shorten
* the immediate length to 1.
*/
immlen = 1;
if (save) {
/* Make the byte form permanent. */
insn->opcode.opcode[0] = insn->opcode.opcode[1];
imm->len = 1;
if (insn->opcode.opcode[2] != 0) {
insn->opcode.opcode[1] = insn->opcode.opcode[2];
insn->opcode.len++;
}
} else if (insn->opcode.opcode[2] != 0)
bc->len++;
}
/* Not really necessary, but saves confusion over it. */
if (save)
insn->postop = X86_POSTOP_NONE;
break;
case X86_POSTOP_SIGNEXT_IMM32:
/* Handle signext_imm32 postop special-casing */
if (!num || yasm_intnum_check_size(num, 32, 0, 1)) {
bc->len++; /* Due to ModRM byte */
immlen = 4;
if (save) {
/* Throwaway REX byte */
unsigned char rex_temp = 0;
/* Build ModRM EA - CAUTION: this depends on
* opcode 0 being a mov instruction!
*/
insn->x86_ea = yasm_x86__ea_create_reg(
(unsigned long)insn->opcode.opcode[0]-0xB8,
&rex_temp, 64);
/* Make the imm32s form permanent. */
insn->opcode.opcode[0] = insn->opcode.opcode[1];
imm->len = 4;
}
}
/* Not really necessary, but saves confusion over it. */
if (save)
insn->postop = X86_POSTOP_NONE;
break;
case X86_POSTOP_SHIFT:
/* Handle shift postop special-casing */
if (num && yasm_intnum_get_uint(num) == 1) {
/* We can use the ,1 form: no imm (set to 0 len) */
immlen = 0;
if (save) {
/* Make the ,1 form permanent. */
insn->opcode.opcode[0] = insn->opcode.opcode[1];
/* Delete imm, as it's not needed. */
yasm_value_delete(&imm->val);
yasm_xfree(imm);
insn->imm = (yasm_immval *)NULL;
}
} else
retval = YASM_BC_RESOLVE_NONE; /* could still get ,1 */
/* Not really necessary, but saves confusion over it. */
if (save)
insn->postop = X86_POSTOP_NONE;
break;
default:
break;
}
yasm_expr_destroy(temp);
bc->len += immlen;
}
bc->len += insn->opcode.len;
bc->len += x86_common_resolve(&insn->common);
bc->len += (insn->special_prefix != 0) ? 1:0;
if (insn->rex != 0xff &&
(insn->rex != 0 ||
(insn->common.mode_bits == 64 && insn->common.opersize == 64 &&
insn->def_opersize_64 != 64)))
bc->len++;
return retval;
}
static yasm_bc_resolve_flags
x86_bc_jmp_resolve(yasm_bytecode *bc, int save,
yasm_calc_bc_dist_func calc_bc_dist)
{
x86_jmp *jmp = (x86_jmp *)bc->contents;
yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN;
yasm_bytecode *target_prevbc;
/*@null@*/ yasm_expr *temp;
/*@only@*/ yasm_intnum *num;
/*@dependent@*/ /*@null@*/ yasm_intnum *num2;
long rel;
unsigned char opersize;
x86_jmp_opcode_sel jrtype = JMP_NONE;
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmp->common.opersize == 0) ?
jmp->common.mode_bits : jmp->common.opersize;
/* We only check to see if forced forms are actually legal if we're in
* save mode. Otherwise we assume that they are legal.
*/
switch (jmp->op_sel) {
case JMP_SHORT_FORCED:
/* 1 byte relative displacement */
jrtype = JMP_SHORT;
if (save) {
/* does a short form exist? */
if (jmp->shortop.len == 0) {
yasm__error(bc->line, N_("short jump does not exist"));
return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN;
}
if (!jmp->target.rel)
num = yasm_intnum_create_uint(0);
else if (!yasm_symrec_get_label(jmp->target.rel, &target_prevbc)
|| target_prevbc->section != jmp->origin_prevbc->section
|| !(num = calc_bc_dist(jmp->origin_prevbc,
target_prevbc))) {
/* External or out of segment, so we can't check distance.
* This depends on the objfmt supporting 8-bit relocs.
* While most don't, some might, so allow it. The objfmt
* will error if not supported.
*/
break;
}
if (jmp->target.abs) {
temp = yasm_expr_copy(jmp->target.abs);
num2 = yasm_expr_get_intnum(&temp, calc_bc_dist);
if (!num2) {
yasm_expr_destroy(temp);
yasm__error(bc->line, N_("jump target too complex"));
return YASM_BC_RESOLVE_ERROR |
YASM_BC_RESOLVE_UNKNOWN_LEN;
}
yasm_intnum_calc(num, YASM_EXPR_ADD, num2, bc->line);
yasm_expr_destroy(temp);
}
rel = yasm_intnum_get_int(num);
yasm_intnum_destroy(num);
rel -= jmp->shortop.len+1;
/* short displacement must fit in -128 <= rel <= +127 */
if (rel < -128 || rel > 127) {
yasm__error(bc->line, N_("short jump out of range"));
return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN;
}
}
break;
case JMP_NEAR_FORCED:
/* 2/4 byte relative displacement (depending on operand size) */
jrtype = JMP_NEAR;
if (save) {
if (jmp->nearop.len == 0) {
yasm__error(bc->line, N_("near jump does not exist"));
return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN;
}
}
break;
default:
/* Due to code in x86_finalize_jmp(), we can only get here if
* there's BOTH short and near opcodes available, and it wasn't
* forced by the user.
*/
if (!jmp->target.rel)
num = yasm_intnum_create_uint(0);
else if (!yasm_symrec_get_label(jmp->target.rel, &target_prevbc)
|| target_prevbc->section != jmp->origin_prevbc->section
|| !(num = calc_bc_dist(jmp->origin_prevbc,
target_prevbc))) {
/* It's unknown. Thus, assume near displacement. */
jrtype = JMP_NEAR;
retval = YASM_BC_RESOLVE_NONE;
break;
}
/* Try to find shortest displacement based on difference between
* target expr value and origin offset.
*/
if (jmp->target.abs) {
temp = yasm_expr_copy(jmp->target.abs);
num2 = yasm_expr_get_intnum(&temp, calc_bc_dist);
if (!num2) {
yasm_expr_destroy(temp);
yasm__error(bc->line, N_("jump target too complex"));
return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN;
}
yasm_intnum_calc(num, YASM_EXPR_ADD, num2, bc->line);
yasm_expr_destroy(temp);
}
rel = yasm_intnum_get_int(num);
yasm_intnum_destroy(num);
rel -= jmp->shortop.len+1;
/* short displacement must fit within -128 <= rel <= +127 */
if (rel >= -128 && rel <= 127) {
/* It fits into a short displacement. */
jrtype = JMP_SHORT;
} else {
/* Near for now, but could get shorter in the future as
* there's a short form available.
*/
jrtype = JMP_NEAR;
retval = YASM_BC_RESOLVE_NONE;
}
break;
}
switch (jrtype) {
case JMP_SHORT:
if (save)
jmp->op_sel = JMP_SHORT;
if (jmp->shortop.len == 0)
return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */
bc->len += jmp->shortop.len + 1;
break;
case JMP_NEAR:
if (save)
jmp->op_sel = JMP_NEAR;
if (jmp->nearop.len == 0)
return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */
bc->len += jmp->nearop.len;
bc->len += (opersize == 16) ? 2 : 4;
break;
default:
yasm_internal_error(N_("unknown jump type"));
}
bc->len += x86_common_resolve(&jmp->common);
return retval;
}
static yasm_bc_resolve_flags
x86_bc_jmpfar_resolve(yasm_bytecode *bc, int save,
yasm_calc_bc_dist_func calc_bc_dist)
{
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_resolve(&jmpfar->common);
return YASM_BC_RESOLVE_MIN_LEN;
}
static void
x86_common_tobytes(const x86_common *common, unsigned char **bufp,
unsigned int segreg)
{
if (common->lockrep_pre != 0)
YASM_WRITE_8(*bufp, common->lockrep_pre);
if (segreg != 0)
YASM_WRITE_8(*bufp, (unsigned char)segreg);
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->addrsize != 0 && common->addrsize != common->mode_bits)
YASM_WRITE_8(*bufp, 0x67);
}
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_immval *imm = insn->imm;
unsigned char *bufp_orig = *bufp;
/* Prefixes */
if (insn->special_prefix != 0)
YASM_WRITE_8(*bufp, insn->special_prefix);
x86_common_tobytes(&insn->common, bufp,
x86_ea ? (x86_ea->ea.segreg>>8) : 0);
if (insn->rex != 0xff) {
if (insn->common.mode_bits == 64 && insn->common.opersize == 64 &&
insn->def_opersize_64 != 64)
insn->rex |= 0x48;
if (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) {
x86_effaddr eat = *x86_ea; /* structure copy */
unsigned char addrsize = insn->common.addrsize;
eat.valid_modrm = 0; /* force checkea to actually run */
if (x86_ea->ea.disp.abs) {
/* Call checkea() to simplify the registers out of the
* displacement. Throw away all of the return values except
* for the modified expr.
*/
if (yasm_x86__expr_checkea
(&eat, &addrsize, insn->common.mode_bits,
insn->postop == X86_POSTOP_ADDRESS16, &insn->rex,
yasm_common_calc_bc_dist, bc->line))
yasm_internal_error(N_("checkea failed"));
x86_ea->ea.disp.abs = eat.ea.disp.abs;
}
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, x86_ea->ea.disp_len,
(size_t)(x86_ea->ea.disp_len*8), 0,
(unsigned long)(*bufp-bufp_orig), bc, 1, d))
return 1;
*bufp += x86_ea->ea.disp_len;
}
}
/* Immediate (if required) */
if (imm) {
if (output_value(&imm->val, *bufp, imm->len, (size_t)(imm->len*8), 0,
(unsigned long)(*bufp-bufp_orig), bc, imm->sign?-1: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 to see if forced 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);
if (output_value(&jmp->target, *bufp, 1, 8, 0,
(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(bc->line, 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);
if (output_value(&jmp->target, *bufp, i, i*8, 0,
(unsigned long)(*bufp-bufp_orig), bc, -1, d))
return 1;
*bufp += i;
break;
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;
if (output_value(&jmpfar->offset, *bufp, i, i*8, 0,
(unsigned long)(*bufp-bufp_orig), bc, 1, d))
return 1;
*bufp += i;
if (output_value(&jmpfar->segment, *bufp, 2, 2*8, 0,
(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,
unsigned long line)
{
/* Write value out. */
yasm_intnum_get_sized(intn, buf, destsize, valsize, shift, 0, warn,
line);
return 0;
}