Yasm Assembler mainline development tree (ffmpeg 依赖)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

878 lines
23 KiB

/*
* x86 bytecode utility functions
*
* Copyright (C) 2001 Peter Johnson
*
* This file is part of YASM.
*
* YASM is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* YASM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "util.h"
/*@unused@*/ RCSID("$IdPath$");
#include "file.h"
#include "globals.h"
#include "errwarn.h"
#include "intnum.h"
#include "expr.h"
#include "bytecode.h"
#include "arch.h"
#include "x86arch.h"
#include "bc-int.h"
/*@-compmempass -mustfree@*/
bytecode *
x86_bc_new_insn(x86_new_insn_data *d)
{
bytecode *bc;
x86_insn *insn;
bc = bc_new_common((bytecode_type)X86_BC_INSN, sizeof(x86_insn));
insn = bc_get_data(bc);
insn->ea = d->ea;
if (d->ea) {
x86_effaddr_data *ead = ea_get_data(d->ea);
ead->modrm &= 0xC7; /* zero spare/reg bits */
ead->modrm |= (d->spare << 3) & 0x38; /* plug in provided bits */
}
if (d->imm) {
insn->imm = imm_new_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->lockrep_pre = 0;
insn->shift_op = d->shift_op;
insn->signext_imm8_op = d->signext_imm8_op;
insn->mode_bits = x86_mode_bits;
return bc;
}
/*@=compmempass =mustfree@*/
/*@-compmempass -mustfree@*/
bytecode *
x86_bc_new_jmprel(x86_new_jmprel_data *d)
{
bytecode *bc;
x86_jmprel *jmprel;
bc = bc_new_common((bytecode_type)X86_BC_JMPREL, sizeof(x86_jmprel));
jmprel = bc_get_data(bc);
jmprel->target = d->target;
jmprel->op_sel = d->op_sel;
if ((d->op_sel == JR_SHORT_FORCED) && (d->near_op_len == 0))
Error(_("no SHORT form of that jump instruction exists"));
if ((d->op_sel == JR_NEAR_FORCED) && (d->short_op_len == 0))
Error(_("no NEAR form of that jump instruction exists"));
jmprel->shortop.opcode[0] = d->short_op[0];
jmprel->shortop.opcode[1] = d->short_op[1];
jmprel->shortop.opcode[2] = d->short_op[2];
jmprel->shortop.opcode_len = d->short_op_len;
jmprel->nearop.opcode[0] = d->near_op[0];
jmprel->nearop.opcode[1] = d->near_op[1];
jmprel->nearop.opcode[2] = d->near_op[2];
jmprel->nearop.opcode_len = d->near_op_len;
jmprel->addrsize = d->addrsize;
jmprel->opersize = d->opersize;
jmprel->lockrep_pre = 0;
jmprel->mode_bits = x86_mode_bits;
return bc;
}
/*@=compmempass =mustfree@*/
void
x86_ea_set_segment(effaddr *ea, unsigned char segment)
{
x86_effaddr_data *ead;
if (!ea)
return;
ead = ea_get_data(ea);
if (segment != 0 && ead->segment != 0)
Warning(_("multiple segment overrides, using leftmost"));
ead->segment = segment;
}
effaddr *
x86_ea_new_reg(unsigned char reg)
{
effaddr *ea = xmalloc(sizeof(effaddr)+sizeof(x86_effaddr_data));
x86_effaddr_data *ead = ea_get_data(ea);
ea->disp = (expr *)NULL;
ea->len = 0;
ea->nosplit = 0;
ead->segment = 0;
ead->modrm = 0xC0 | (reg & 0x07); /* Mod=11, R/M=Reg, Reg=0 */
ead->valid_modrm = 1;
ead->need_modrm = 1;
ead->sib = 0;
ead->valid_sib = 0;
ead->need_sib = 0;
return ea;
}
effaddr *
x86_ea_new_expr(expr *e)
{
effaddr *ea = xmalloc(sizeof(effaddr)+sizeof(x86_effaddr_data));
x86_effaddr_data *ead = ea_get_data(ea);
ea->disp = e;
ea->len = 0;
ea->nosplit = 0;
ead->segment = 0;
ead->modrm = 0;
ead->valid_modrm = 0;
ead->need_modrm = 1;
ead->sib = 0;
ead->valid_sib = 0;
ead->need_sib = 0xff; /* we won't know until we know more about expr and
the BITS/address override setting */
return ea;
}
/*@-compmempass@*/
effaddr *
x86_ea_new_imm(expr *imm, unsigned char im_len)
{
effaddr *ea = xmalloc(sizeof(effaddr)+sizeof(x86_effaddr_data));
x86_effaddr_data *ead = ea_get_data(ea);
ea->disp = imm;
ea->len = im_len;
ea->nosplit = 0;
ead->segment = 0;
ead->modrm = 0;
ead->valid_modrm = 0;
ead->need_modrm = 0;
ead->sib = 0;
ead->valid_sib = 0;
ead->need_sib = 0;
return ea;
}
/*@=compmempass@*/
effaddr *
x86_bc_insn_get_ea(bytecode *bc)
{
x86_insn *insn = bc_get_data(bc);
if (!bc)
return NULL;
if ((x86_bytecode_type)bc->type != X86_BC_INSN)
InternalError(_("Trying to get EA of non-instruction"));
return insn->ea;
}
void
x86_bc_insn_opersize_override(bytecode *bc, unsigned char opersize)
{
x86_insn *insn;
x86_jmprel *jmprel;
if (!bc)
return;
switch ((x86_bytecode_type)bc->type) {
case X86_BC_INSN:
insn = bc_get_data(bc);
insn->opersize = opersize;
break;
case X86_BC_JMPREL:
jmprel = bc_get_data(bc);
jmprel->opersize = opersize;
break;
default:
InternalError(_("OperSize override applied to non-instruction"));
}
}
void
x86_bc_insn_addrsize_override(bytecode *bc, unsigned char addrsize)
{
x86_insn *insn;
x86_jmprel *jmprel;
if (!bc)
return;
switch ((x86_bytecode_type)bc->type) {
case X86_BC_INSN:
insn = bc_get_data(bc);
insn->addrsize = addrsize;
break;
case X86_BC_JMPREL:
jmprel = bc_get_data(bc);
jmprel->addrsize = addrsize;
break;
default:
InternalError(_("AddrSize override applied to non-instruction"));
}
}
void
x86_bc_insn_set_lockrep_prefix(bytecode *bc, unsigned char prefix)
{
x86_insn *insn;
x86_jmprel *jmprel;
unsigned char *lockrep_pre = (unsigned char *)NULL;
if (!bc)
return;
switch ((x86_bytecode_type)bc->type) {
case X86_BC_INSN:
insn = bc_get_data(bc);
lockrep_pre = &insn->lockrep_pre;
break;
case X86_BC_JMPREL:
jmprel = bc_get_data(bc);
lockrep_pre = &jmprel->lockrep_pre;
break;
default:
InternalError(_("LockRep prefix applied to non-instruction"));
}
if (*lockrep_pre != 0)
Warning(_("multiple LOCK or REP prefixes, using leftmost"));
*lockrep_pre = prefix;
}
void
x86_set_jmprel_opcode_sel(x86_jmprel_opcode_sel *old_sel,
x86_jmprel_opcode_sel new_sel)
{
if (!old_sel)
return;
if (new_sel != JR_NONE && ((*old_sel == JR_SHORT_FORCED) ||
(*old_sel == JR_NEAR_FORCED)))
Warning(_("multiple SHORT or NEAR specifiers, using leftmost"));
*old_sel = new_sel;
}
void
x86_bc_delete(bytecode *bc)
{
x86_insn *insn;
x86_jmprel *jmprel;
switch ((x86_bytecode_type)bc->type) {
case X86_BC_INSN:
insn = bc_get_data(bc);
if (insn->ea)
ea_delete(insn->ea);
if (insn->imm) {
expr_delete(insn->imm->val);
xfree(insn->imm);
}
break;
case X86_BC_JMPREL:
jmprel = bc_get_data(bc);
expr_delete(jmprel->target);
break;
}
}
void
x86_ea_data_print(FILE *f, const effaddr *ea)
{
const x86_effaddr_data *ead = ea_get_const_data(ea);
fprintf(f, "%*sSegmentOv=%02x\n", indent_level, "",
(unsigned int)ead->segment);
fprintf(f, "%*sModRM=%03o ValidRM=%u NeedRM=%u\n", indent_level, "",
(unsigned int)ead->modrm, (unsigned int)ead->valid_modrm,
(unsigned int)ead->need_modrm);
fprintf(f, "%*sSIB=%03o ValidSIB=%u NeedSIB=%u\n", indent_level, "",
(unsigned int)ead->sib, (unsigned int)ead->valid_sib,
(unsigned int)ead->need_sib);
}
void
x86_bc_print(FILE *f, const bytecode *bc)
{
const x86_insn *insn;
const x86_jmprel *jmprel;
switch ((x86_bytecode_type)bc->type) {
case X86_BC_INSN:
insn = bc_get_const_data(bc);
fprintf(f, "%*s_Instruction_\n", indent_level, "");
fprintf(f, "%*sEffective Address:", indent_level, "");
if (insn->ea) {
fprintf(f, "\n");
indent_level++;
ea_print(f, insn->ea);
indent_level--;
} 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)
expr_print(f, insn->imm->val);
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 ShiftOp=%u\n",
indent_level, "",
(unsigned int)insn->addrsize,
(unsigned int)insn->opersize,
(unsigned int)insn->lockrep_pre,
(unsigned int)insn->shift_op);
fprintf(f, "%*sBITS=%u\n", indent_level, "",
(unsigned int)insn->mode_bits);
break;
case X86_BC_JMPREL:
jmprel = bc_get_const_data(bc);
fprintf(f, "%*s_Relative Jump_\n", indent_level, "");
fprintf(f, "%*sTarget=", indent_level, "");
expr_print(f, jmprel->target);
fprintf(f, "\n%*sShort Form:\n", indent_level, "");
indent_level++;
if (jmprel->shortop.opcode_len == 0)
fprintf(f, "%*sNone\n", indent_level, "");
else
fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n",
indent_level, "",
(unsigned int)jmprel->shortop.opcode[0],
(unsigned int)jmprel->shortop.opcode[1],
(unsigned int)jmprel->shortop.opcode[2],
(unsigned int)jmprel->shortop.opcode_len);
indent_level--;
fprintf(f, "%*sNear Form:\n", indent_level, "");
indent_level++;
if (jmprel->nearop.opcode_len == 0)
fprintf(f, "%*sNone\n", indent_level, "");
else
fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n",
indent_level, "",
(unsigned int)jmprel->nearop.opcode[0],
(unsigned int)jmprel->nearop.opcode[1],
(unsigned int)jmprel->nearop.opcode[2],
(unsigned int)jmprel->nearop.opcode_len);
indent_level--;
fprintf(f, "%*sOpSel=", indent_level, "");
switch (jmprel->op_sel) {
case JR_NONE:
fprintf(f, "None");
break;
case JR_SHORT:
fprintf(f, "Short");
break;
case JR_NEAR:
fprintf(f, "Near");
break;
case JR_SHORT_FORCED:
fprintf(f, "Forced Short");
break;
case JR_NEAR_FORCED:
fprintf(f, "Forced Near");
break;
default:
fprintf(f, "UNKNOWN!!");
break;
}
fprintf(f, "\n%*sAddrSize=%u OperSize=%u LockRepPre=%02x\n",
indent_level, "",
(unsigned int)jmprel->addrsize,
(unsigned int)jmprel->opersize,
(unsigned int)jmprel->lockrep_pre);
fprintf(f, "%*sBITS=%u\n", indent_level, "",
(unsigned int)jmprel->mode_bits);
break;
}
}
static bc_resolve_flags
x86_bc_resolve_insn(x86_insn *insn, unsigned long *len, int save,
const section *sect, calc_bc_dist_func calc_bc_dist)
{
/*@null@*/ expr *temp;
effaddr *ea = insn->ea;
x86_effaddr_data *ead = ea_get_data(ea);
immval *imm = insn->imm;
bc_resolve_flags retval = BC_RESOLVE_MIN_LEN;
if (ea) {
/* Create temp copy of disp, etc. */
x86_effaddr_data ead_t = *ead; /* structure copy */
unsigned char displen = ea->len;
if (ea->disp) {
temp = expr_copy(ea->disp);
assert(temp != NULL);
/* 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 (!x86_expr_checkea(&temp, &insn->addrsize, insn->mode_bits,
ea->nosplit, &displen, &ead_t.modrm,
&ead_t.valid_modrm, &ead_t.need_modrm,
&ead_t.sib, &ead_t.valid_sib,
&ead_t.need_sib, calc_bc_dist)) {
expr_delete(temp);
/* failed, don't bother checking rest of insn */
return BC_RESOLVE_UNKNOWN_LEN;
}
expr_delete(temp);
if (displen != 1) {
/* Fits into a word/dword, or unknown. */
retval = BC_RESOLVE_NONE; /* may not be smallest size */
/* Handle unknown case, make displen word-sized */
if (displen == 0xff)
displen = (insn->addrsize == 32) ? 4U : 2U;
}
if (save) {
*ead = ead_t; /* structure copy */
ea->len = displen;
if (displen == 0 && ea->disp) {
expr_delete(ea->disp);
ea->disp = NULL;
}
}
}
/* Compute length of ea and add to total */
*len += ead_t.need_modrm + ead_t.need_sib + displen;
*len += (ead_t.segment != 0) ? 1 : 0;
}
if (imm) {
const intnum *num;
unsigned int immlen = imm->len;
if (imm->val) {
temp = 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 = expr_get_intnum(&temp, calc_bc_dist))) {
if (num && intnum_get_uint(num) == 1) {
/* We can use the ,1 form: subtract out the imm len
* (as we add it back in below).
*/
*len -= imm->len;
if (save) {
/* Make the ,1 form permanent. */
insn->opcode[0] = insn->opcode[1];
/* Delete imm, as it's not needed. */
expr_delete(imm->val);
xfree(imm);
insn->imm = (immval *)NULL;
}
} else
retval = BC_RESOLVE_NONE; /* we could still get ,1 */
/* Not really necessary, but saves confusion over it. */
if (save)
insn->shift_op = 0;
}
expr_delete(temp);
}
*len += immlen;
}
*len += insn->opcode_len;
*len += (insn->addrsize != 0 && insn->addrsize != insn->mode_bits) ? 1:0;
*len += (insn->opersize != 0 && insn->opersize != insn->mode_bits) ? 1:0;
*len += (insn->lockrep_pre != 0) ? 1:0;
return retval;
}
static bc_resolve_flags
x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save,
const bytecode *bc, const section *sect,
calc_bc_dist_func calc_bc_dist)
{
bc_resolve_flags retval = BC_RESOLVE_MIN_LEN;
/*@null@*/ expr *temp;
/*@dependent@*/ /*@null@*/ const intnum *num;
long rel;
unsigned char opersize;
int jrshort = 0;
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmprel->opersize == 0) ? jmprel->mode_bits :
jmprel->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 (jmprel->op_sel) {
case JR_SHORT_FORCED:
/* 1 byte relative displacement */
jrshort = 1;
if (save) {
temp = expr_copy(jmprel->target);
num = expr_get_intnum(&temp, calc_bc_dist);
if (!num) {
ErrorAt(bc->line,
_("short jump target external or out of segment"));
return BC_RESOLVE_ERROR | BC_RESOLVE_UNKNOWN_LEN;
} else {
rel = intnum_get_int(num);
rel -= jmprel->shortop.opcode_len+1;
/* does a short form exist? */
if (jmprel->shortop.opcode_len == 0) {
ErrorAt(bc->line, _("short jump does not exist"));
return BC_RESOLVE_ERROR | BC_RESOLVE_UNKNOWN_LEN;
}
/* short displacement must fit in -128 <= rel <= +127 */
if (rel < -128 || rel > 127) {
ErrorAt(bc->line, _("short jump out of range"));
return BC_RESOLVE_ERROR | BC_RESOLVE_UNKNOWN_LEN;
}
}
}
break;
case JR_NEAR_FORCED:
/* 2/4 byte relative displacement (depending on operand size) */
jrshort = 0;
if (save) {
if (jmprel->nearop.opcode_len == 0) {
ErrorAt(bc->line, _("near jump does not exist"));
return BC_RESOLVE_ERROR | BC_RESOLVE_UNKNOWN_LEN;
}
}
break;
default:
/* 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 = expr_copy(jmprel->target);
num = expr_get_intnum(&temp, calc_bc_dist);
if (num) {
rel = intnum_get_int(num);
rel -= jmprel->shortop.opcode_len+1;
/* short displacement must fit within -128 <= rel <= +127 */
if (jmprel->shortop.opcode_len != 0 && rel >= -128 &&
rel <= 127) {
/* It fits into a short displacement. */
jrshort = 1;
} else if (jmprel->nearop.opcode_len != 0) {
/* Near for now, but could get shorter in the future if
* there's a short form available.
*/
jrshort = 0;
if (jmprel->shortop.opcode_len != 0)
retval = 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) {
ErrorAt(bc->line, _("short jump out of range (near jump does not exist)"));
return BC_RESOLVE_ERROR | BC_RESOLVE_UNKNOWN_LEN;
}
jrshort = 1;
}
} 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 (jmprel->nearop.opcode_len != 0) {
if (jmprel->shortop.opcode_len != 0)
retval = BC_RESOLVE_NONE;
jrshort = 0;
} else {
if (save) {
ErrorAt(bc->line,
_("short jump out of range (near jump does not exist)"));
return BC_RESOLVE_ERROR | BC_RESOLVE_UNKNOWN_LEN;
}
jrshort = 1;
}
}
expr_delete(temp);
break;
}
if (jrshort) {
if (save)
jmprel->op_sel = JR_SHORT;
if (jmprel->shortop.opcode_len == 0)
return BC_RESOLVE_UNKNOWN_LEN; /* uh-oh, that size not available */
*len += jmprel->shortop.opcode_len + 1;
} else {
if (save)
jmprel->op_sel = JR_NEAR;
if (jmprel->nearop.opcode_len == 0)
return BC_RESOLVE_UNKNOWN_LEN; /* uh-oh, that size not available */
*len += jmprel->nearop.opcode_len;
*len += (opersize == 32) ? 4 : 2;
}
*len += (jmprel->addrsize != 0 && jmprel->addrsize != jmprel->mode_bits) ?
1:0;
*len += (jmprel->opersize != 0 && jmprel->opersize != jmprel->mode_bits) ?
1:0;
*len += (jmprel->lockrep_pre != 0) ? 1:0;
return retval;
}
bc_resolve_flags
x86_bc_resolve(bytecode *bc, int save, const section *sect,
calc_bc_dist_func calc_bc_dist)
{
x86_insn *insn;
x86_jmprel *jmprel;
switch ((x86_bytecode_type)bc->type) {
case X86_BC_INSN:
insn = bc_get_data(bc);
return x86_bc_resolve_insn(insn, &bc->len, save, sect,
calc_bc_dist);
case X86_BC_JMPREL:
jmprel = bc_get_data(bc);
return x86_bc_resolve_jmprel(jmprel, &bc->len, save, bc, sect,
calc_bc_dist);
default:
break;
}
InternalError(_("Didn't handle bytecode type in x86 arch"));
/*@notreached@*/
return BC_RESOLVE_UNKNOWN_LEN;
}
static int
x86_bc_tobytes_insn(x86_insn *insn, unsigned char **bufp, const section *sect,
const bytecode *bc, void *d, output_expr_func output_expr)
{
/*@null@*/ effaddr *ea = insn->ea;
x86_effaddr_data *ead = ea_get_data(ea);
immval *imm = insn->imm;
unsigned int i;
/* Prefixes */
if (insn->lockrep_pre != 0)
WRITE_BYTE(*bufp, insn->lockrep_pre);
if (ea && ead->segment != 0)
WRITE_BYTE(*bufp, ead->segment);
if (insn->opersize != 0 && insn->opersize != insn->mode_bits)
WRITE_BYTE(*bufp, 0x66);
if (insn->addrsize != 0 && insn->addrsize != insn->mode_bits)
WRITE_BYTE(*bufp, 0x67);
/* Opcode */
for (i=0; i<insn->opcode_len; i++)
WRITE_BYTE(*bufp, insn->opcode[i]);
/* Effective address: ModR/M (if required), SIB (if required), and
* displacement (if required).
*/
if (ea) {
if (ead->need_modrm) {
if (!ead->valid_modrm)
InternalError(_("invalid Mod/RM in x86 tobytes_insn"));
WRITE_BYTE(*bufp, ead->modrm);
}
if (ead->need_sib) {
if (!ead->valid_sib)
InternalError(_("invalid SIB in x86 tobytes_insn"));
WRITE_BYTE(*bufp, ead->sib);
}
if (ea->disp) {
x86_effaddr_data ead_t = *ead; /* structure copy */
unsigned char displen = ea->len;
unsigned char addrsize = insn->addrsize;
ead_t.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 (!x86_expr_checkea(&ea->disp, &addrsize, insn->mode_bits,
ea->nosplit, &displen, &ead_t.modrm,
&ead_t.valid_modrm, &ead_t.need_modrm,
&ead_t.sib, &ead_t.valid_sib,
&ead_t.need_sib, common_calc_bc_dist))
InternalError(_("checkea failed"));
if (ea->disp) {
if (output_expr(&ea->disp, bufp, ea->len, sect, bc, 0, d))
return 1;
} 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++)
WRITE_BYTE(*bufp, 0);
}
}
}
/* Immediate (if required) */
if (imm && imm->val) {
/* TODO: check imm->len vs. sized len from expr? */
if (output_expr(&imm->val, bufp, imm->len, sect, bc, 0, d))
return 1;
}
return 0;
}
static int
x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp,
const section *sect, const bytecode *bc, void *d,
output_expr_func output_expr)
{
unsigned char opersize;
unsigned int i;
/* Prefixes */
if (jmprel->lockrep_pre != 0)
WRITE_BYTE(*bufp, jmprel->lockrep_pre);
/* FIXME: branch hints! */
if (jmprel->opersize != 0 && jmprel->opersize != jmprel->mode_bits)
WRITE_BYTE(*bufp, 0x66);
if (jmprel->addrsize != 0 && jmprel->addrsize != jmprel->mode_bits)
WRITE_BYTE(*bufp, 0x67);
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmprel->opersize == 0) ? jmprel->mode_bits :
jmprel->opersize;
/* Check here to see if forced forms are actually legal. */
switch (jmprel->op_sel) {
case JR_SHORT_FORCED:
case JR_SHORT:
/* 1 byte relative displacement */
if (jmprel->shortop.opcode_len == 0)
InternalError(_("short jump does not exist"));
/* Opcode */
for (i=0; i<jmprel->shortop.opcode_len; i++)
WRITE_BYTE(*bufp, jmprel->shortop.opcode[i]);
/* Relative displacement */
if (output_expr(&jmprel->target, bufp, 1, sect, bc, 1, d))
return 1;
break;
case JR_NEAR_FORCED:
case JR_NEAR:
/* 2/4 byte relative displacement (depending on operand size) */
if (jmprel->nearop.opcode_len == 0) {
ErrorAt(bc->line, _("near jump does not exist"));
return 1;
}
/* Opcode */
for (i=0; i<jmprel->nearop.opcode_len; i++)
WRITE_BYTE(*bufp, jmprel->nearop.opcode[i]);
/* Relative displacement */
if (output_expr(&jmprel->target, bufp,
(opersize == 32) ? 4UL : 2UL, sect, bc, 1, d))
return 1;
break;
default:
InternalError(_("unrecognized relative jump op_sel"));
}
return 0;
}
int
x86_bc_tobytes(bytecode *bc, unsigned char **bufp, const section *sect,
void *d, output_expr_func output_expr)
{
x86_insn *insn;
x86_jmprel *jmprel;
switch ((x86_bytecode_type)bc->type) {
case X86_BC_INSN:
insn = bc_get_data(bc);
return x86_bc_tobytes_insn(insn, bufp, sect, bc, d, output_expr);
case X86_BC_JMPREL:
jmprel = bc_get_data(bc);
return x86_bc_tobytes_jmprel(jmprel, bufp, sect, bc, d,
output_expr);
default:
break;
}
return 0;
}