Logo Search packages:      
Sourcecode: yasm version File versions  Download package

x86bc.c

/*
 * 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: x86bc.c 1168 2004-10-31 01:07:52Z peter $");

#define YASM_LIB_INTERNAL
#define YASM_BC_INTERNAL
#define YASM_EXPR_INTERNAL
#include <libyasm.h>

#include "x86arch.h"


/* Effective address type */

typedef struct x86_effaddr {
    yasm_effaddr ea;          /* base structure */

    /* PC-relative portions are for AMD64 only (RIP addressing) */
    /*@null@*/ /*@dependent@*/ yasm_symrec *origin;    /* pcrel origin */

    unsigned char segment;    /* segment override, 0 if none */

    /* How the spare (register) bits in Mod/RM are handled:
     * Even if valid_modrm=0, the spare bits are still valid (don't overwrite!)
     * They're set in bytecode_create_insn().
     */
    unsigned char modrm;
    unsigned char valid_modrm;      /* 1 if Mod/RM byte currently valid, 0 if not */
    unsigned char need_modrm; /* 1 if Mod/RM byte needed, 0 if not */

    unsigned char sib;
    unsigned char valid_sib;  /* 1 if SIB byte currently valid, 0 if not */
    unsigned char need_sib;   /* 1 if SIB byte needed, 0 if not,
                           0xff if unknown */

    unsigned char pcrel;      /* 1 if PC-relative transformation needed */
} x86_effaddr;

/* Bytecode types */

typedef struct x86_insn {
    yasm_bytecode bc;         /* base structure */

    /*@null@*/ x86_effaddr *ea;     /* effective address */

    /*@null@*/ yasm_immval *imm;/* immediate or relative value */

    unsigned char opcode[3];  /* opcode */
    unsigned char opcode_len;

    unsigned char addrsize;   /* 0 or =mode_bits => no override */
    unsigned char opersize;   /* 0 or =mode_bits => no override */
    unsigned char lockrep_pre;      /* 0 indicates no prefix */

    unsigned char def_opersize_64;  /* default operand size in 64-bit mode */
    unsigned char special_prefix;   /* "special" prefix (0=none) */

    unsigned char rex;        /* REX AMD64 extension, 0 if none,
                           0xff if not allowed (high 8 bit reg used) */

    /* HACK, but a space-saving one: shift opcodes have an immediate
     * form and a ,1 form (with no immediate).  In the parser, we
     * set this and opcode_len=1, but store the ,1 version in the
     * second byte of the opcode array.  We then choose between the
     * two versions once we know the actual value of imm (because we
     * don't know it in the parser module).
     *
     * A override to force the imm version should just leave this at
     * 0.  Then later code won't know the ,1 version even exists.
     * TODO: Figure out how this affects CPU flags processing.
     *
     * Call x86_SetInsnShiftFlag() to set this flag to 1.
     */
    unsigned char shift_op;

    /* HACK, similar to that for shift_op above, for optimizing instructions
     * that take a sign-extended imm8 as well as imm values (eg, the arith
     * instructions and a subset of the imul instructions).
     */
    unsigned char signext_imm8_op;

    /* HACK, similar to those above, for optimizing long (modrm+sib) mov
     * instructions in amd64 into short mov instructions if a 32-bit address
     * override is applied in 64-bit mode to an EA of just an offset (no
     * registers) and the target register is al/ax/eax/rax.
     */
    unsigned char shortmov_op;

    unsigned char mode_bits;
} x86_insn;

typedef struct x86_jmp {
    yasm_bytecode bc;         /* base structure */

    yasm_expr *target;        /* target location */
    /*@dependent@*/ yasm_symrec *origin;    /* jump origin */

    struct {
      unsigned char opcode[3];
      unsigned char opcode_len;   /* 0 = no opc for this version */
    } shortop, nearop, farop;

    /* which opcode are we using? */
    /* The *FORCED forms are specified in the source as such */
    x86_jmp_opcode_sel op_sel;

    unsigned char addrsize;   /* 0 or =mode_bits => no override */
    unsigned char opersize;   /* 0 indicates no override */
    unsigned char lockrep_pre;      /* 0 indicates no prefix */

    unsigned char mode_bits;
} x86_jmp;

/* 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(yasm_bytecode *bc);
static void x86_bc_insn_print(const yasm_bytecode *bc, 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_expr_func output_expr,
                         /*@null@*/ yasm_output_reloc_func output_reloc);

static void x86_bc_jmp_destroy(yasm_bytecode *bc);
static void x86_bc_jmp_print(const yasm_bytecode *bc, 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_expr_func output_expr,
                        /*@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,
    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,
    x86_bc_jmp_resolve,
    x86_bc_jmp_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;
}

/*@-compmempass -mustfree@*/
yasm_bytecode *
yasm_x86__bc_create_insn(yasm_arch *arch, x86_new_insn_data *d)
{
    yasm_arch_x86 *arch_x86 = (yasm_arch_x86 *)arch;
    x86_insn *insn;
   
    insn = (x86_insn *)yasm_bc_create_common(&x86_bc_callback_insn,
                                   sizeof(x86_insn), d->line);

    insn->ea = (x86_effaddr *)d->ea;
    if (d->ea) {
      insn->ea->origin = d->ea_origin;
      insn->ea->modrm &= 0xC7;      /* zero spare/reg bits */
      insn->ea->modrm |= (d->spare << 3) & 0x38;  /* plug in provided bits */
    }

    if (d->imm) {
      insn->imm = yasm_imm_create_expr(d->imm);
      insn->imm->len = d->im_len;
      insn->imm->sign = d->im_sign;
    } else
      insn->imm = NULL;

    insn->opcode[0] = d->op[0];
    insn->opcode[1] = d->op[1];
    insn->opcode[2] = d->op[2];
    insn->opcode_len = d->op_len;

    insn->addrsize = 0;
    insn->opersize = d->opersize;
    insn->def_opersize_64 = d->def_opersize_64;
    insn->special_prefix = d->special_prefix;
    insn->lockrep_pre = 0;
    insn->rex = d->rex;
    insn->shift_op = d->shift_op;
    insn->signext_imm8_op = d->signext_imm8_op;
    insn->shortmov_op = d->shortmov_op;

    insn->mode_bits = arch_x86->mode_bits;

    return (yasm_bytecode *)insn;
}
/*@=compmempass =mustfree@*/

/*@-compmempass -mustfree@*/
yasm_bytecode *
yasm_x86__bc_create_jmp(yasm_arch *arch, x86_new_jmp_data *d)
{
    yasm_arch_x86 *arch_x86 = (yasm_arch_x86 *)arch;
    x86_jmp *jmp;

    jmp = (x86_jmp *) yasm_bc_create_common(&x86_bc_callback_jmp,
                                  sizeof(x86_jmp), d->line);

    jmp->target = d->target;
    jmp->origin = d->origin;
    jmp->op_sel = d->op_sel;

    if ((d->op_sel == JMP_SHORT_FORCED) && (d->near_op_len == 0))
      yasm__error(d->line,
                N_("no SHORT form of that jump instruction exists"));
    if ((d->op_sel == JMP_NEAR_FORCED) && (d->short_op_len == 0))
      yasm__error(d->line,
                N_("no NEAR form of that jump instruction exists"));

    jmp->shortop.opcode[0] = d->short_op[0];
    jmp->shortop.opcode[1] = d->short_op[1];
    jmp->shortop.opcode[2] = d->short_op[2];
    jmp->shortop.opcode_len = d->short_op_len;

    jmp->nearop.opcode[0] = d->near_op[0];
    jmp->nearop.opcode[1] = d->near_op[1];
    jmp->nearop.opcode[2] = d->near_op[2];
    jmp->nearop.opcode_len = d->near_op_len;

    jmp->farop.opcode[0] = d->far_op[0];
    jmp->farop.opcode[1] = d->far_op[1];
    jmp->farop.opcode[2] = d->far_op[2];
    jmp->farop.opcode_len = d->far_op_len;

    jmp->addrsize = d->addrsize;
    jmp->opersize = d->opersize;
    jmp->lockrep_pre = 0;

    jmp->mode_bits = arch_x86->mode_bits;

    return (yasm_bytecode *)jmp;
}
/*@=compmempass =mustfree@*/

void
yasm_x86__ea_set_segment(yasm_effaddr *ea, unsigned int segment,
                   unsigned long line)
{
    x86_effaddr *x86_ea = (x86_effaddr *)ea;

    if (!ea)
      return;

    if (segment != 0 && x86_ea->segment != 0)
      yasm__warning(YASM_WARN_GENERAL, line,
                  N_("multiple segment overrides, using leftmost"));

    x86_ea->segment = (unsigned char)segment;
}

void
yasm_x86__ea_set_disponly(yasm_effaddr *ea)
{
    x86_effaddr *x86_ea = (x86_effaddr *)ea;

    x86_ea->valid_modrm = 0;
    x86_ea->need_modrm = 0;
    x86_ea->valid_sib = 0;
    x86_ea->need_sib = 0;
    x86_ea->pcrel = 0;
}

yasm_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;
    x86_ea->ea.disp = (yasm_expr *)NULL;
    x86_ea->ea.len = 0;
    x86_ea->ea.nosplit = 0;
    x86_ea->segment = 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;
    x86_ea->pcrel = 0;

    return (yasm_effaddr *)x86_ea;
}

yasm_effaddr *
yasm_x86__ea_create_expr(yasm_arch *arch, yasm_expr *e)
{
    x86_effaddr *x86_ea;

    x86_ea = yasm_xmalloc(sizeof(x86_effaddr));

    x86_ea->ea.callback = &x86_ea_callback;
    x86_ea->ea.disp = e;
    x86_ea->ea.len = 0;
    x86_ea->ea.nosplit = 0;
    x86_ea->segment = 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;
    x86_ea->pcrel = 0;

    return (yasm_effaddr *)x86_ea;
}

/*@-compmempass@*/
yasm_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;
    x86_ea->ea.disp = imm;
    x86_ea->ea.len = (unsigned char)im_len;
    x86_ea->ea.nosplit = 0;
    x86_ea->segment = 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->pcrel = 0;

    return (yasm_effaddr *)x86_ea;
}
/*@=compmempass@*/

yasm_effaddr *
yasm_x86__bc_insn_get_ea(yasm_bytecode *bc)
{
    if (!bc)
      return NULL;

    if (bc->callback != &x86_bc_callback_insn)
      yasm_internal_error(N_("Trying to get EA of non-instruction"));

    return (yasm_effaddr *)(((x86_insn *)bc)->ea);
}

void
yasm_x86__bc_insn_opersize_override(yasm_bytecode *bc, unsigned int opersize)
{
    if (!bc)
      return;

    if (bc->callback == &x86_bc_callback_insn) {
      x86_insn *insn = (x86_insn *)bc;
      insn->opersize = (unsigned char)opersize;
    } else if (bc->callback == &x86_bc_callback_jmp) {
      x86_jmp *jmp = (x86_jmp *)bc;
      jmp->opersize = (unsigned char)opersize;
    } else
      yasm_internal_error(N_("OperSize override applied to non-instruction"));
}

void
yasm_x86__bc_insn_addrsize_override(yasm_bytecode *bc, unsigned int addrsize)
{
    if (!bc)
      return;

    if (bc->callback == &x86_bc_callback_insn) {
      x86_insn *insn = (x86_insn *)bc;
      insn->addrsize = (unsigned char)addrsize;
    } else if (bc->callback == &x86_bc_callback_jmp) {
      x86_jmp *jmp = (x86_jmp *)bc;
      jmp->addrsize = (unsigned char)addrsize;
    } else
      yasm_internal_error(N_("AddrSize override applied to non-instruction"));
}

void
yasm_x86__bc_insn_set_lockrep_prefix(yasm_bytecode *bc, unsigned int prefix,
                             unsigned long line)
{
    unsigned char *lockrep_pre = (unsigned char *)NULL;

    if (!bc)
      return;

    if (bc->callback == &x86_bc_callback_insn) {
      x86_insn *insn = (x86_insn *)bc;
      lockrep_pre = &insn->lockrep_pre;
    } else if (bc->callback == &x86_bc_callback_jmp) {
      x86_jmp *jmp = (x86_jmp *)bc;
      lockrep_pre = &jmp->lockrep_pre;
    } else
      yasm_internal_error(N_("LockRep prefix applied to non-instruction"));

    if (*lockrep_pre != 0)
      yasm__warning(YASM_WARN_GENERAL, line,
                  N_("multiple LOCK or REP prefixes, using leftmost"));

    *lockrep_pre = (unsigned char)prefix;
}

static void
x86_bc_insn_destroy(yasm_bytecode *bc)
{
    x86_insn *insn = (x86_insn *)bc;
    if (insn->ea)
      yasm_ea_destroy((yasm_effaddr *)insn->ea);
    if (insn->imm) {
      yasm_expr_destroy(insn->imm->val);
      yasm_xfree(insn->imm);
    }
}

static void
x86_bc_jmp_destroy(yasm_bytecode *bc)
{
    x86_jmp *jmp = (x86_jmp *)bc;
    yasm_expr_destroy(jmp->target);
}

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 PCRel=%u\n", indent_level, "",
          (unsigned int)x86_ea->segment, (unsigned int)x86_ea->pcrel);
    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_bc_insn_print(const yasm_bytecode *bc, FILE *f, int indent_level)
{
    const x86_insn *insn = (const x86_insn *)bc;

    fprintf(f, "%*s_Instruction_\n", indent_level, "");
    fprintf(f, "%*sEffective Address:", indent_level, "");
    if (insn->ea) {
      fprintf(f, "\n");
      yasm_ea_print((yasm_effaddr *)insn->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%*sVal=", indent_level, "");
      if (insn->imm->val)
          yasm_expr_print(insn->imm->val, f);
      else
          fprintf(f, "(nil-SHOULDN'T HAPPEN)");
      fprintf(f, "\n");
      fprintf(f, "%*sLen=%u, Sign=%u\n", indent_level, "",
            (unsigned int)insn->imm->len,
            (unsigned int)insn->imm->sign);
      indent_level--;
    }
    fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n", indent_level,
          "", (unsigned int)insn->opcode[0],
          (unsigned int)insn->opcode[1],
          (unsigned int)insn->opcode[2],
          (unsigned int)insn->opcode_len);
    fprintf(f,
          "%*sAddrSize=%u OperSize=%u LockRepPre=%02x SpPre=%02x REX=%03o\n",
          indent_level, "",
          (unsigned int)insn->addrsize,
          (unsigned int)insn->opersize,
          (unsigned int)insn->lockrep_pre,
          (unsigned int)insn->special_prefix,
          (unsigned int)insn->rex);
    fprintf(f, "%*sShiftOp=%u BITS=%u\n", indent_level, "",
          (unsigned int)insn->shift_op,
          (unsigned int)insn->mode_bits);
}

static void
x86_bc_jmp_print(const yasm_bytecode *bc, FILE *f, int indent_level)
{
    const x86_jmp *jmp = (const x86_jmp *)bc;

    fprintf(f, "%*s_Jump_\n", indent_level, "");
    fprintf(f, "%*sTarget=", indent_level, "");
    yasm_expr_print(jmp->target, f);
    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.opcode_len == 0)
      fprintf(f, "%*sNone\n", indent_level+1, "");
    else
      fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n",
            indent_level+1, "",
            (unsigned int)jmp->shortop.opcode[0],
            (unsigned int)jmp->shortop.opcode[1],
            (unsigned int)jmp->shortop.opcode[2],
            (unsigned int)jmp->shortop.opcode_len);
    fprintf(f, "%*sNear Form:\n", indent_level, "");
    if (jmp->nearop.opcode_len == 0)
      fprintf(f, "%*sNone\n", indent_level+1, "");
    else
      fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n",
            indent_level+1, "",
            (unsigned int)jmp->nearop.opcode[0],
            (unsigned int)jmp->nearop.opcode[1],
            (unsigned int)jmp->nearop.opcode[2],
            (unsigned int)jmp->nearop.opcode_len);
    fprintf(f, "%*sFar Form:\n", indent_level, "");
    if (jmp->farop.opcode_len == 0)
      fprintf(f, "%*sNone\n", indent_level+1, "");
    else
      fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n",
            indent_level+1, "",
            (unsigned int)jmp->farop.opcode[0],
            (unsigned int)jmp->farop.opcode[1],
            (unsigned int)jmp->farop.opcode[2],
            (unsigned int)jmp->farop.opcode_len);
    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;
      case JMP_FAR:
          fprintf(f, "Far");
          break;
      default:
          fprintf(f, "UNKNOWN!!");
          break;
    }
    fprintf(f, "\n%*sAddrSize=%u OperSize=%u LockRepPre=%02x\n",
          indent_level, "",
          (unsigned int)jmp->addrsize,
          (unsigned int)jmp->opersize,
          (unsigned int)jmp->lockrep_pre);
    fprintf(f, "%*sBITS=%u\n", indent_level, "",
          (unsigned int)jmp->mode_bits);
}

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;
    /*@null@*/ yasm_expr *temp;
    x86_effaddr *x86_ea = insn->ea;
    yasm_effaddr *ea = &x86_ea->ea;
    yasm_immval *imm = insn->imm;
    yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN;

    if (ea) {
      /* Create temp copy of disp, etc. */
      x86_effaddr eat = *x86_ea;  /* structure copy */
      unsigned char displen = ea->len;

      if (ea->disp) {
          temp = yasm_expr_copy(ea->disp);
          assert(temp != NULL);

          /* Handle shortmov special-casing */
          if (insn->shortmov_op && insn->mode_bits == 64 &&
            insn->addrsize == 32 &&
            !yasm_expr__contains(temp, YASM_EXPR_REG)) {
            yasm_x86__ea_set_disponly((yasm_effaddr *)&eat);

            if (save) {
                /* Make the short form permanent. */
                insn->opcode[0] = insn->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(&temp, &insn->addrsize,
                insn->mode_bits, ea->nosplit, &displen, &eat.modrm,
                &eat.valid_modrm, &eat.need_modrm, &eat.sib,
                &eat.valid_sib, &eat.need_sib, &eat.pcrel, &insn->rex,
                calc_bc_dist)) {
            case 1:
                yasm_expr_destroy(temp);
                /* failed, don't bother checking rest of insn */
                return YASM_BC_RESOLVE_UNKNOWN_LEN|YASM_BC_RESOLVE_ERROR;
            case 2:
                yasm_expr_destroy(temp);
                /* failed, don't bother checking rest of insn */
                return YASM_BC_RESOLVE_UNKNOWN_LEN;
            default:
                yasm_expr_destroy(temp);
                /* okay */
                break;
          }

          if (displen != 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 (displen == 0xff)
                displen = (insn->addrsize == 16) ? 2U : 4U;
          }

          /* If we had forced ea->len but had to override, save it now */
          if (ea->len != 0 && ea->len != displen)
            ea->len = displen;

          if (save) {
            *x86_ea = eat;    /* structure copy */
            ea->len = displen;
            if (displen == 0 && ea->disp) {
                yasm_expr_destroy(ea->disp);
                ea->disp = NULL;
            }
          }
      }

      /* Compute length of ea and add to total */
      bc->len += eat.need_modrm + (eat.need_sib ? 1:0) + displen;
      bc->len += (eat.segment != 0) ? 1 : 0;
    }

    if (imm) {
      const yasm_intnum *num;
      unsigned int immlen = imm->len;

      if (imm->val) {
          temp = yasm_expr_copy(imm->val);
          assert(temp != NULL);

          /* TODO: check imm->len vs. sized len from expr? */

          /* Handle shift_op special-casing */
          if (insn->shift_op && temp &&
            (num = yasm_expr_get_intnum(&temp, calc_bc_dist))) {
            if (num && yasm_intnum_get_uint(num) == 1) {
                /* We can use the ,1 form: subtract out the imm len
                 * (as we add it back in below).
                 */
                bc->len -= imm->len;

                if (save) {
                  /* Make the ,1 form permanent. */
                  insn->opcode[0] = insn->opcode[1];
                  /* Delete imm, as it's not needed. */
                  yasm_expr_destroy(imm->val);
                  yasm_xfree(imm);
                  insn->imm = (yasm_immval *)NULL;
                }
            } else
                retval = YASM_BC_RESOLVE_NONE;  /* we could still get ,1 */

            /* Not really necessary, but saves confusion over it. */
            if (save)
                insn->shift_op = 0;
          }

          yasm_expr_destroy(temp);
      }

      bc->len += immlen;
    }

    bc->len += insn->opcode_len;
    bc->len += (insn->addrsize != 0 && insn->addrsize != insn->mode_bits) ? 1:0;
    if (insn->opersize != 0 &&
      ((insn->mode_bits != 64 && insn->opersize != insn->mode_bits) ||
       (insn->mode_bits == 64 && insn->opersize == 16)))
      bc->len++;
    bc->len += (insn->special_prefix != 0) ? 1:0;
    bc->len += (insn->lockrep_pre != 0) ? 1:0;
    if (insn->rex != 0xff &&
      (insn->rex != 0 ||
       (insn->mode_bits == 64 && insn->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;
    yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN;
    /*@null@*/ yasm_expr *temp;
    /*@dependent@*/ /*@null@*/ const yasm_intnum *num;
    long rel;
    unsigned char opersize;
    x86_jmp_opcode_sel jrtype = JMP_NONE;

    /* As opersize may be 0, figure out its "real" value. */
    opersize = (jmp->opersize == 0) ? jmp->mode_bits : jmp->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) {
            temp = yasm_expr_copy(jmp->target);
            temp = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(temp),
                              yasm_expr_sym(jmp->origin), bc->line);
            num = yasm_expr_get_intnum(&temp, calc_bc_dist);
            if (!num) {
                yasm__error(bc->line,
                  N_("short jump target external or out of segment"));
                yasm_expr_destroy(temp);
                return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN;
            } else {
                rel = yasm_intnum_get_int(num);
                rel -= jmp->shortop.opcode_len+1;
                yasm_expr_destroy(temp);
                /* does a short form exist? */
                if (jmp->shortop.opcode_len == 0) {
                  yasm__error(bc->line, N_("short jump does not exist"));
                  return YASM_BC_RESOLVE_ERROR |
                      YASM_BC_RESOLVE_UNKNOWN_LEN;
                }
                /* 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.opcode_len == 0) {
                yasm__error(bc->line, N_("near jump does not exist"));
                return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN;
            }
          }
          break;
      default:
          temp = yasm_expr_copy(jmp->target);
          temp = yasm_expr_simplify(temp, NULL);

          /* Check for far displacement (seg:off). */
          if (yasm_expr_is_op(temp, YASM_EXPR_SEGOFF)) {
            jrtype = JMP_FAR;
            break;          /* length handled below */
          } else if (jmp->op_sel == JMP_FAR) {
            yasm__error(bc->line,
                      N_("far jump does not have a far displacement"));
            return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN;
          }

          /* Try to find shortest displacement based on difference between
           * target expr value and our (this bytecode's) offset.  Note this
           * requires offset to be set BEFORE calling calc_len in order for
           * this test to be valid.
           */
          temp = yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(temp),
                            yasm_expr_sym(jmp->origin), bc->line);
          num = yasm_expr_get_intnum(&temp, calc_bc_dist);
          if (num) {
            rel = yasm_intnum_get_int(num);
            rel -= jmp->shortop.opcode_len+1;
            /* short displacement must fit within -128 <= rel <= +127 */
            if (jmp->shortop.opcode_len != 0 && rel >= -128 &&
                rel <= 127) {
                /* It fits into a short displacement. */
                jrtype = JMP_SHORT;
            } else if (jmp->nearop.opcode_len != 0) {
                /* Near for now, but could get shorter in the future if
                 * there's a short form available.
                 */
                jrtype = JMP_NEAR;
                if (jmp->shortop.opcode_len != 0)
                  retval = YASM_BC_RESOLVE_NONE;
            } else {
                /* Doesn't fit into short, and there's no near opcode.
                 * Error out if saving, otherwise just make it a short
                 * (in the hopes that a short might make it possible for
                 * it to actually be within short range).
                 */
                if (save) {
                  yasm__error(bc->line,
                      N_("short jump out of range (near jump does not exist)"));
                  return YASM_BC_RESOLVE_ERROR |
                      YASM_BC_RESOLVE_UNKNOWN_LEN;
                }
                jrtype = JMP_SHORT;
            }
          } else {
            /* It's unknown.  Thus, assume near displacement.  If a near
             * opcode is not available, use a short opcode instead.
             * If we're saving, error if a near opcode is not available.
             */
            if (jmp->nearop.opcode_len != 0) {
                if (jmp->shortop.opcode_len != 0)
                  retval = YASM_BC_RESOLVE_NONE;
                jrtype = JMP_NEAR;
            } else {
                if (save) {
                  yasm__error(bc->line,
                      N_("short jump out of range (near jump does not exist)"));
                  return YASM_BC_RESOLVE_ERROR |
                      YASM_BC_RESOLVE_UNKNOWN_LEN;
                }
                jrtype = JMP_SHORT;
            }
          }
          yasm_expr_destroy(temp);
          break;
    }

    switch (jrtype) {
      case JMP_SHORT:
          if (save)
            jmp->op_sel = JMP_SHORT;
          if (jmp->shortop.opcode_len == 0)
            return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */

          bc->len += jmp->shortop.opcode_len + 1;
          break;
      case JMP_NEAR:
          if (save)
            jmp->op_sel = JMP_NEAR;
          if (jmp->nearop.opcode_len == 0)
            return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */

          bc->len += jmp->nearop.opcode_len;
          bc->len += (opersize == 16) ? 2 : 4;
          break;
      case JMP_FAR:
          if (save)
            jmp->op_sel = JMP_FAR;
          if (jmp->farop.opcode_len == 0)
            return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */

          bc->len += jmp->farop.opcode_len;
          bc->len += 2; /* segment */
          bc->len += (opersize == 16) ? 2 : 4;
          break;
      default:
          yasm_internal_error(N_("unknown jump type"));
    }
    bc->len += (jmp->addrsize != 0 && jmp->addrsize != jmp->mode_bits) ? 1:0;
    bc->len += (jmp->opersize != 0 && jmp->opersize != jmp->mode_bits) ? 1:0;
    bc->len += (jmp->lockrep_pre != 0) ? 1:0;

    return retval;
}

static int
x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
                yasm_output_expr_func output_expr,
                /*@unused@*/ yasm_output_reloc_func output_reloc)
{
    x86_insn *insn = (x86_insn *)bc;
    /*@null@*/ x86_effaddr *x86_ea = insn->ea;
    /*@null@*/ yasm_effaddr *ea = &x86_ea->ea;
    yasm_immval *imm = insn->imm;
    unsigned int i;
    unsigned char *bufp_orig = *bufp;

    /* Prefixes */
    if (insn->special_prefix != 0)
      YASM_WRITE_8(*bufp, insn->special_prefix);
    if (insn->lockrep_pre != 0)
      YASM_WRITE_8(*bufp, insn->lockrep_pre);
    if (x86_ea && x86_ea->segment != 0)
      YASM_WRITE_8(*bufp, x86_ea->segment);
    if (insn->opersize != 0 &&
      ((insn->mode_bits != 64 && insn->opersize != insn->mode_bits) ||
       (insn->mode_bits == 64 && insn->opersize == 16)))
      YASM_WRITE_8(*bufp, 0x66);
    if (insn->addrsize != 0 && insn->addrsize != insn->mode_bits)
      YASM_WRITE_8(*bufp, 0x67);
    if (insn->rex != 0xff) {
      if (insn->mode_bits == 64 && insn->opersize == 64 &&
          insn->def_opersize_64 != 64)
          insn->rex |= 0x48;
      if (insn->rex != 0) {
          if (insn->mode_bits != 64)
            yasm_internal_error(
                N_("x86: got a REX prefix in non-64-bit mode"));
          YASM_WRITE_8(*bufp, insn->rex);
      }
    }

    /* Opcode */
    for (i=0; i<insn->opcode_len; i++)
      YASM_WRITE_8(*bufp, insn->opcode[i]);

    /* Effective address: ModR/M (if required), SIB (if required), and
     * displacement (if required).
     */
    if (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 (ea->disp) {
          x86_effaddr eat = *x86_ea;  /* structure copy */
          unsigned char displen = ea->len;
          unsigned char addrsize = insn->addrsize;

          eat.valid_modrm = 0;    /* force checkea to actually run */

          /* 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(&ea->disp, &addrsize, insn->mode_bits,
                               ea->nosplit, &displen, &eat.modrm,
                               &eat.valid_modrm, &eat.need_modrm,
                               &eat.sib, &eat.valid_sib,
                               &eat.need_sib, &eat.pcrel, &insn->rex,
                               yasm_common_calc_bc_dist))
            yasm_internal_error(N_("checkea failed"));

          if (ea->disp) {
            if (eat.pcrel) {
                /*@null@*/ yasm_expr *wrt = yasm_expr_extract_wrt(&ea->disp);
                ea->disp =
                  yasm_expr_create(YASM_EXPR_SUB,
                               yasm_expr_expr(ea->disp),
                               yasm_expr_sym(eat.origin), bc->line);
                if (wrt) {
                  ea->disp =
                      yasm_expr_create(YASM_EXPR_WRT,
                                   yasm_expr_expr(ea->disp),
                                   yasm_expr_expr(wrt), bc->line);
                }
                if (output_expr(&ea->disp, *bufp, ea->len,
                            (size_t)(ea->len*8), 0,
                            (unsigned long)(*bufp-bufp_orig), bc, 1, 1,
                            d))
                  return 1;
            } else {
                if (output_expr(&ea->disp, *bufp, ea->len,
                            (size_t)(ea->len*8), 0,
                            (unsigned long)(*bufp-bufp_orig), bc, 0, 1,
                            d))
                  return 1;
            }
            *bufp += ea->len;
          } else {
            /* 0 displacement, but we didn't know it before, so we have to
             * write out 0 value.
             */
            for (i=0; i<ea->len; i++)
                YASM_WRITE_8(*bufp, 0);
          }
      }
    }

    /* Immediate (if required) */
    if (imm && imm->val) {
      if (output_expr(&imm->val, *bufp, imm->len, (size_t)(imm->len*8), 0,
                  (unsigned long)(*bufp-bufp_orig), bc, 0, 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_expr_func output_expr,
               /*@unused@*/ yasm_output_reloc_func output_reloc)
{
    x86_jmp *jmp = (x86_jmp *)bc;
    unsigned char opersize;
    unsigned int i;
    unsigned char *bufp_orig = *bufp;
    /*@null@*/ yasm_expr *targetseg;
    /*@null@*/ yasm_expr *wrt;
    yasm_expr *dup;

    /* Prefixes */
    if (jmp->lockrep_pre != 0)
      YASM_WRITE_8(*bufp, jmp->lockrep_pre);
    /* FIXME: branch hints! */
    if (jmp->opersize != 0 && jmp->opersize != jmp->mode_bits)
      YASM_WRITE_8(*bufp, 0x66);
    if (jmp->addrsize != 0 && jmp->addrsize != jmp->mode_bits)
      YASM_WRITE_8(*bufp, 0x67);

    /* As opersize may be 0, figure out its "real" value. */
    opersize = (jmp->opersize == 0) ? jmp->mode_bits : jmp->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.opcode_len == 0)
            yasm_internal_error(N_("short jump does not exist"));

          /* Opcode */
          for (i=0; i<jmp->shortop.opcode_len; i++)
            YASM_WRITE_8(*bufp, jmp->shortop.opcode[i]);

          /* Relative displacement */
          wrt = yasm_expr_extract_wrt(&jmp->target);
          jmp->target =
            yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(jmp->target),
                         yasm_expr_sym(jmp->origin), bc->line);
          if (wrt)
            jmp->target = yasm_expr_create_tree(jmp->target,
                                        YASM_EXPR_WRT, wrt,
                                        bc->line);
          if (output_expr(&jmp->target, *bufp, 1, 8, 0,
                      (unsigned long)(*bufp-bufp_orig), bc, 1, 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.opcode_len == 0) {
            yasm__error(bc->line, N_("near jump does not exist"));
            return 1;
          }

          /* Opcode */
          for (i=0; i<jmp->nearop.opcode_len; i++)
            YASM_WRITE_8(*bufp, jmp->nearop.opcode[i]);

          /* Relative displacement */
          wrt = yasm_expr_extract_wrt(&jmp->target);
          jmp->target =
            yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(jmp->target),
                         yasm_expr_sym(jmp->origin), bc->line);
          if (wrt)
            jmp->target = yasm_expr_create_tree(jmp->target,
                                        YASM_EXPR_WRT, wrt,
                                        bc->line);
          i = (opersize == 16) ? 2 : 4;
          if (output_expr(&jmp->target, *bufp, i, i*8, 0,
                      (unsigned long)(*bufp-bufp_orig), bc, 1, 1, d))
            return 1;
          *bufp += i;
          break;
      case JMP_FAR:
          /* far absolute (4/6 byte depending on operand size) */
          if (jmp->farop.opcode_len == 0) {
            yasm__error(bc->line, N_("far jump does not exist"));
            return 1;
          }

          /* Opcode */
          for (i=0; i<jmp->farop.opcode_len; i++)
            YASM_WRITE_8(*bufp, jmp->farop.opcode[i]);

          /* Absolute displacement: segment and offset */
          jmp->target = yasm_expr_simplify(jmp->target, NULL);
          dup = yasm_expr_copy(jmp->target);
          targetseg = yasm_expr_extract_segoff(&dup);
          if (!targetseg)
            yasm_internal_error(N_("could not extract segment for far jump"));
          i = (opersize == 16) ? 2 : 4;
          if (output_expr(&dup, *bufp, i, i*8, 0,
                      (unsigned long)(*bufp-bufp_orig), bc, 0, 1, d))
            return 1;
          *bufp += i;
          if (output_expr(&targetseg, *bufp, 2, 2*8, 0,
                      (unsigned long)(*bufp-bufp_orig), bc, 0, 1, d))
            return 1;
          *bufp += 2;

          yasm_expr_destroy(dup);
          yasm_expr_destroy(targetseg);

          break;
      default:
          yasm_internal_error(N_("unrecognized relative jump op_sel"));
    }
    return 0;
}

int
yasm_x86__intnum_fixup_rel(yasm_arch *arch, yasm_intnum *intn, size_t valsize,
                     const yasm_bytecode *bc, unsigned long line)
{
    yasm_intnum *delta;
    if (valsize != 8 && valsize != 16 && valsize != 32)
      yasm_internal_error(
          N_("tried to do PC-relative offset from invalid sized value"));
    delta = yasm_intnum_create_uint(bc->len);
    yasm_intnum_calc(intn, YASM_EXPR_SUB, delta, line);
    yasm_intnum_destroy(delta);
    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;
}

Generated by  Doxygen 1.6.0   Back to index