* \file bytecode.h
* \brief YASM bytecode interface.
*
- * $IdPath: yasm/libyasm/bytecode.h,v 1.73 2003/05/04 22:15:09 peter Exp $
+ * $IdPath$
*
* Copyright (C) 2001 Peter Johnson
*
* \return Newly allocated buffer that should be used instead of buf for
* reading the byte representation, or NULL if buf was big enough to
* hold the entire byte representation.
+ * \caution Essentially destroys contents of bytecode, so it's NOT safe to call
+ * twice on the same bytecode.
*/
/*@null@*/ /*@only@*/ unsigned char *yasm_bc_tobytes
(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize,
* \file coretype.h
* \brief YASM core types and utility functions.
*
- * $IdPath: yasm/libyasm/coretype.h,v 1.23 2003/03/31 08:22:05 peter Exp $
+ * $IdPath$
*
* Copyright (C) 2001 Peter Johnson
*
/** Expression operators usable in #yasm_expr expressions. */
typedef enum {
+ YASM_EXPR_IDENT, /**< No operation, just a value. */
YASM_EXPR_ADD, /**< Arithmetic addition (+). */
YASM_EXPR_SUB, /**< Arithmetic subtraction (-). */
YASM_EXPR_MUL, /**< Arithmetic multiplication (*). */
YASM_EXPR_LE, /**< Less than or equal to comparison. */
YASM_EXPR_GE, /**< Greater than or equal to comparison. */
YASM_EXPR_NE, /**< Not equal comparison. */
+ YASM_EXPR_NONNUM, /**< Start of non-numeric operations (not an op). */
YASM_EXPR_SEG, /**< SEG operator (gets segment portion of address). */
YASM_EXPR_WRT, /**< WRT operator (gets offset of address relative to
* some other segment). */
- YASM_EXPR_SEGOFF, /**< The ':' in segment:offset. */
- YASM_EXPR_IDENT /**< No operation, just a value. */
+ YASM_EXPR_SEGOFF /**< The ':' in segment:offset. */
} yasm_expr_op;
/** Symbol record visibility.
yasm_xfree(e);
e = sube;
}
+
+ /* If non-numeric expression, don't fold constants. */
+ if (e->op > YASM_EXPR_NONNUM)
+ fold_const = 0;
+
level_numterms = e->numterms;
level_fold_numterms = 0;
for (i=0; i<e->numterms; i++) {
}
/*@=mustfree@*/
+int
+yasm_expr_is_op(const yasm_expr *e, yasm_expr_op op)
+{
+ return (e->op == op);
+}
+
static int
expr_contains_callback(const yasm_expr__item *ei, void *d)
{
return sym;
}
+yasm_expr *
+yasm_expr_extract_segment(yasm_expr **ep)
+{
+ yasm_expr *retval;
+ yasm_expr *e = *ep;
+
+ /* If not SEG:OFF, we can't do this transformation */
+ if (e->op != YASM_EXPR_SEGOFF)
+ return NULL;
+
+ /* Extract the SEG portion out to its own expression */
+ if (e->terms[0].type == YASM_EXPR_EXPR)
+ retval = e->terms[0].data.expn;
+ else {
+ /* Need to build IDENT expression to hold non-expression contents */
+ retval = yasm_xmalloc(sizeof(yasm_expr));
+ retval->op = YASM_EXPR_IDENT;
+ retval->numterms = 1;
+ retval->terms[0] = e->terms[0]; /* structure copy */
+ }
+
+ /* Delete the SEG: portion by changing the expression into an IDENT */
+ e->op = YASM_EXPR_IDENT;
+ e->numterms = 1;
+ e->terms[0] = e->terms[1]; /* structure copy */
+
+ return retval;
+}
+
/*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/
const yasm_intnum *
yasm_expr_get_intnum(yasm_expr **ep, yasm_calc_bc_dist_func calc_bc_dist)
case YASM_EXPR_IDENT:
opstr[0] = 0;
break;
+ default:
+ strcpy(opstr, " !UNK! ");
+ break;
}
for (i=0; i<e->numterms; i++) {
switch (e->terms[i].type) {
* \file expr.h
* \brief YASM expression interface
*
- * $IdPath: yasm/libyasm/expr.h,v 1.40 2003/05/04 20:28:28 peter Exp $
+ * $IdPath$
*
* Copyright (C) 2001 Michael Urman, Peter Johnson
*
*/
void yasm_expr_delete(/*@only@*/ /*@null@*/ yasm_expr *e);
+/** Determine if an expression is a specified operation (at the top level).
+ * \param e expression
+ * \param op operator
+ * \return Nonzero if the expression was the specified operation at the top
+ * level, zero otherwise.
+ */
+int yasm_expr_is_op(const yasm_expr *e, yasm_expr_op op);
+
/** Extra transformation function for yasm_expr__level_tree().
* \param e expression being simplified
* \param d data provided as expr_xform_extra_data to
/*@dependent@*/ /*@null@*/ yasm_symrec *yasm_expr_extract_symrec
(yasm_expr **ep, yasm_calc_bc_dist_func calc_bc_dist);
+/** Extract the segment portion of a SEG:OFF expression, leaving the offset.
+ * \param ep expression (pointer to)
+ * \return NULL if unable to extract a segment (YASM_EXPR_SEGOFF not the
+ * top-level operator), otherwise the segment expression. The input
+ * expression is modified such that on return, it's the offset
+ * expression.
+ */
+/*@only@*/ /*@null@*/ yasm_expr *yasm_expr_extract_segment(yasm_expr **e);
+
/** Get the integer value of an expression if it's just an integer.
* \param ep expression (pointer to)
* \param calc_bc_dist bytecode distance-calculation function
JR_SHORT,
JR_NEAR,
JR_SHORT_FORCED,
- JR_NEAR_FORCED
+ JR_NEAR_FORCED,
+ JR_FAR /* not really relative, but fits here */
} x86_jmprel_opcode_sel;
typedef enum {
typedef struct x86_new_jmprel_data {
unsigned long lindex;
/*@keep@*/ yasm_expr *target;
+ /*@dependent@*/ yasm_symrec *origin;
x86_jmprel_opcode_sel op_sel;
unsigned char short_op_len;
unsigned char short_op[3];
unsigned char near_op_len;
unsigned char near_op[3];
+ unsigned char far_op_len;
+ unsigned char far_op[3];
unsigned char addrsize;
unsigned char opersize;
} x86_new_jmprel_data;
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <util.h>
-/*@unused@*/ RCSID("$IdPath: yasm/modules/arch/x86/x86bc.c,v 1.52 2003/03/26 05:07:55 peter Exp $");
+/*@unused@*/ RCSID("$IdPath$");
#define YASM_LIB_INTERNAL
#define YASM_BC_INTERNAL
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;
+ } shortop, nearop, farop;
/* which opcode are we using? */
/* The *FORCED forms are specified in the source as such */
sizeof(x86_jmprel), d->lindex);
jmprel->target = d->target;
+ jmprel->origin = d->origin;
jmprel->op_sel = d->op_sel;
if ((d->op_sel == JR_SHORT_FORCED) && (d->near_op_len == 0))
jmprel->nearop.opcode[2] = d->near_op[2];
jmprel->nearop.opcode_len = d->near_op_len;
+ jmprel->farop.opcode[0] = d->far_op[0];
+ jmprel->farop.opcode[1] = d->far_op[1];
+ jmprel->farop.opcode[2] = d->far_op[2];
+ jmprel->farop.opcode_len = d->far_op_len;
+
jmprel->addrsize = d->addrsize;
jmprel->opersize = d->opersize;
jmprel->lockrep_pre = 0;
case JR_NEAR_FORCED:
fprintf(f, "Forced Near");
break;
+ case JR_FAR:
+ fprintf(f, "Far");
+ break;
default:
fprintf(f, "UNKNOWN!!");
break;
/*@dependent@*/ /*@null@*/ const yasm_intnum *num;
long rel;
unsigned char opersize;
- int jrshort = 0;
+ x86_jmprel_opcode_sel jrtype = JR_NONE;
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmprel->opersize == 0) ? jmprel->mode_bits :
switch (jmprel->op_sel) {
case JR_SHORT_FORCED:
/* 1 byte relative displacement */
- jrshort = 1;
+ jrtype = JR_SHORT;
if (save) {
temp = yasm_expr_copy(jmprel->target);
+ temp = yasm_expr_new(YASM_EXPR_SUB, yasm_expr_expr(temp),
+ yasm_expr_sym(jmprel->origin), bc->line);
num = yasm_expr_get_intnum(&temp, calc_bc_dist);
if (!num) {
yasm__error(bc->line,
break;
case JR_NEAR_FORCED:
/* 2/4 byte relative displacement (depending on operand size) */
- jrshort = 0;
+ jrtype = JR_NEAR;
if (save) {
if (jmprel->nearop.opcode_len == 0) {
yasm__error(bc->line, N_("near jump does not exist"));
}
break;
default:
+ temp = yasm_expr_copy(jmprel->target);
+ temp = yasm_expr_simplify(temp, NULL);
+
+ /* Check for far displacement (seg:off). */
+ if (yasm_expr_is_op(temp, YASM_EXPR_SEGOFF)) {
+ jrtype = JR_FAR;
+ break; /* length handled below */
+ } else if (jmprel->op_sel == JR_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_copy(jmprel->target);
+ temp = yasm_expr_new(YASM_EXPR_SUB, yasm_expr_expr(temp),
+ yasm_expr_sym(jmprel->origin), bc->line);
num = yasm_expr_get_intnum(&temp, calc_bc_dist);
if (num) {
rel = yasm_intnum_get_int(num);
if (jmprel->shortop.opcode_len != 0 && rel >= -128 &&
rel <= 127) {
/* It fits into a short displacement. */
- jrshort = 1;
+ jrtype = JR_SHORT;
} 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;
+ jrtype = JR_NEAR;
if (jmprel->shortop.opcode_len != 0)
retval = YASM_BC_RESOLVE_NONE;
} else {
return YASM_BC_RESOLVE_ERROR |
YASM_BC_RESOLVE_UNKNOWN_LEN;
}
- jrshort = 1;
+ jrtype = JR_SHORT;
}
} else {
/* It's unknown. Thus, assume near displacement. If a near
if (jmprel->nearop.opcode_len != 0) {
if (jmprel->shortop.opcode_len != 0)
retval = YASM_BC_RESOLVE_NONE;
- jrshort = 0;
+ jrtype = JR_NEAR;
} else {
if (save) {
yasm__error(bc->line,
return YASM_BC_RESOLVE_ERROR |
YASM_BC_RESOLVE_UNKNOWN_LEN;
}
- jrshort = 1;
+ jrtype = JR_SHORT;
}
}
yasm_expr_delete(temp);
break;
}
- if (jrshort) {
- if (save)
- jmprel->op_sel = JR_SHORT;
- if (jmprel->shortop.opcode_len == 0)
- return YASM_BC_RESOLVE_UNKNOWN_LEN; /* that size not available */
+ switch (jrtype) {
+ case JR_SHORT:
+ if (save)
+ jmprel->op_sel = JR_SHORT;
+ if (jmprel->shortop.opcode_len == 0)
+ return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */
- *len += jmprel->shortop.opcode_len + 1;
- } else {
- if (save)
- jmprel->op_sel = JR_NEAR;
- if (jmprel->nearop.opcode_len == 0)
- return YASM_BC_RESOLVE_UNKNOWN_LEN; /* that size not available */
+ *len += jmprel->shortop.opcode_len + 1;
+ break;
+ case JR_NEAR:
+ if (save)
+ jmprel->op_sel = JR_NEAR;
+ if (jmprel->nearop.opcode_len == 0)
+ return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */
- *len += jmprel->nearop.opcode_len;
- *len += (opersize == 32) ? 4 : 2;
+ *len += jmprel->nearop.opcode_len;
+ *len += (opersize == 32) ? 4 : 2;
+ break;
+ case JR_FAR:
+ if (save)
+ jmprel->op_sel = JR_FAR;
+ if (jmprel->farop.opcode_len == 0)
+ return YASM_BC_RESOLVE_UNKNOWN_LEN; /* size not available */
+
+ *len += jmprel->farop.opcode_len;
+ *len += 2; /* segment */
+ *len += (opersize == 32) ? 4 : 2;
+ break;
+ default:
+ yasm_internal_error(N_("unknown jump type"));
}
*len += (jmprel->addrsize != 0 && jmprel->addrsize != jmprel->mode_bits) ?
1:0;
unsigned char opersize;
unsigned int i;
unsigned char *bufp_orig = *bufp;
+ /*@null@*/ yasm_expr *targetseg;
/* Prefixes */
if (jmprel->lockrep_pre != 0)
YASM_WRITE_8(*bufp, jmprel->shortop.opcode[i]);
/* Relative displacement */
+ jmprel->target =
+ yasm_expr_new(YASM_EXPR_SUB, yasm_expr_expr(jmprel->target),
+ yasm_expr_sym(jmprel->origin), bc->line);
if (output_expr(&jmprel->target, bufp, 1,
(unsigned long)(*bufp-bufp_orig), sect, bc, 1, d))
return 1;
YASM_WRITE_8(*bufp, jmprel->nearop.opcode[i]);
/* Relative displacement */
+ jmprel->target =
+ yasm_expr_new(YASM_EXPR_SUB, yasm_expr_expr(jmprel->target),
+ yasm_expr_sym(jmprel->origin), bc->line);
if (output_expr(&jmprel->target, bufp,
(opersize == 32) ? 4UL : 2UL,
(unsigned long)(*bufp-bufp_orig), sect, bc, 1, d))
return 1;
break;
+ case JR_FAR:
+ /* far absolute (4/6 byte depending on operand size) */
+ if (jmprel->farop.opcode_len == 0) {
+ yasm__error(bc->line, N_("far jump does not exist"));
+ return 1;
+ }
+
+ /* Opcode */
+ for (i=0; i<jmprel->farop.opcode_len; i++)
+ YASM_WRITE_8(*bufp, jmprel->farop.opcode[i]);
+
+ /* Absolute displacement: segment and offset */
+ jmprel->target = yasm_expr_simplify(jmprel->target, NULL);
+ targetseg = yasm_expr_extract_segment(&jmprel->target);
+ if (!targetseg)
+ yasm_internal_error(N_("could not extract segment for far jump"));
+ if (output_expr(&jmprel->target, bufp,
+ (opersize == 32) ? 4UL : 2UL,
+ (unsigned long)(*bufp-bufp_orig), sect, bc, 0, d))
+ return 1;
+ if (output_expr(&targetseg, bufp, 2UL,
+ (unsigned long)(*bufp-bufp_orig), sect, bc, 0, d))
+ return 1;
+
+ break;
default:
yasm_internal_error(N_("unrecognized relative jump op_sel"));
}
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <util.h>
-RCSID("$IdPath: yasm/modules/arch/x86/x86id.re,v 1.46 2003/03/31 05:36:29 peter Exp $");
+RCSID("$IdPath$");
#define YASM_LIB_INTERNAL
#define YASM_BC_INTERNAL
* 0 = none
* 1 = shift operation with a ,1 short form (instead of imm8).
* 2 = large imm16/32 that can become a sign-extended imm8.
+ * 3 = can be far jump
*/
#define OPT_Imm 0x0
#define OPT_Reg 0x1
#define OPAP_None (0UL<<16)
#define OPAP_ShiftOp (1UL<<16)
#define OPAP_SImm8Avail (2UL<<16)
+#define OPAP_JmpFar (3UL<<16)
#define OPAP_MASK (3UL<<16)
typedef struct x86_insn_info {
{ CPU_Any, 0, 16, 0, {0, 0, 0}, 0, 1, {OPT_Imm|OPS_16|OPA_JmpRel, 0, 0} },
{ CPU_386, 0, 32, 0, {0, 0, 0}, 0, 1, {OPT_Imm|OPS_32|OPA_JmpRel, 0, 0} },
- { CPU_Any, 0, 16, 1, {0xE8, 0, 0}, 0, 1,
- {OPT_Imm|OPS_16|OPTM_Near|OPA_JmpRel, 0, 0} },
- { CPU_386, 0, 32, 1, {0xE8, 0, 0}, 0, 1,
- {OPT_Imm|OPS_32|OPTM_Near|OPA_JmpRel, 0, 0} },
- { CPU_Any, 0, 0, 1, {0xE8, 0, 0}, 0, 1,
- {OPT_Imm|OPS_Any|OPTM_Near|OPA_JmpRel, 0, 0} },
+ { CPU_Any, 0, 16, 1, {0xE8, 0x9A, 0}, 0, 1,
+ {OPT_Imm|OPS_16|OPTM_Near|OPA_JmpRel|OPAP_JmpFar, 0, 0} },
+ { CPU_386, 0, 32, 1, {0xE8, 0x9A, 0}, 0, 1,
+ {OPT_Imm|OPS_32|OPTM_Near|OPA_JmpRel|OPAP_JmpFar, 0, 0} },
+ { CPU_Any, 0, 0, 1, {0xE8, 0x9A, 0}, 0, 1,
+ {OPT_Imm|OPS_Any|OPTM_Near|OPA_JmpRel|OPAP_JmpFar, 0, 0} },
{ CPU_Any, 0, 16, 1, {0xFF, 0, 0}, 2, 1, {OPT_RM|OPS_16|OPA_EA, 0, 0} },
{ CPU_386|CPU_Not64, 0, 32, 1, {0xFF, 0, 0}, 2, 1,
{ CPU_Any, 0, 0, 1, {0xFF, 0, 0}, 2, 1,
{OPT_Mem|OPS_Any|OPTM_Near|OPA_EA, 0, 0} },
- /* TODO: Far Imm 16:16/32 */
+ { CPU_Any, 0, 16, 1, {0x9A, 0, 0}, 3, 1,
+ {OPT_Imm|OPS_16|OPTM_Far|OPA_JmpRel, 0, 0} },
+ { CPU_386, 0, 32, 1, {0x9A, 0, 0}, 3, 1,
+ {OPT_Imm|OPS_32|OPTM_Far|OPA_JmpRel, 0, 0} },
+ { CPU_Any, 0, 0, 1, {0x9A, 0, 0}, 3, 1,
+ {OPT_Imm|OPS_Any|OPTM_Far|OPA_JmpRel, 0, 0} },
{ CPU_Any, 0, 16, 1, {0xFF, 0, 0}, 3, 1,
{OPT_Mem|OPS_16|OPTM_Far|OPA_EA, 0, 0} },
{ CPU_Any, 0, 0, 1, {0xEB, 0, 0}, 0, 1,
{OPT_Imm|OPS_Any|OPTM_Short|OPA_JmpRel, 0, 0} },
- { CPU_Any, 0, 16, 1, {0xE9, 0, 0}, 0, 1,
- {OPT_Imm|OPS_16|OPTM_Near|OPA_JmpRel, 0, 0} },
- { CPU_386, 0, 32, 1, {0xE9, 0, 0}, 0, 1,
- {OPT_Imm|OPS_32|OPTM_Near|OPA_JmpRel, 0, 0} },
- { CPU_Any, 0, 0, 1, {0xE9, 0, 0}, 0, 1,
- {OPT_Imm|OPS_Any|OPTM_Near|OPA_JmpRel, 0, 0} },
+ { CPU_Any, 0, 16, 1, {0xE9, 0xEA, 0}, 0, 1,
+ {OPT_Imm|OPS_16|OPTM_Near|OPA_JmpRel|OPAP_JmpFar, 0, 0} },
+ { CPU_386, 0, 32, 1, {0xE9, 0xEA, 0}, 0, 1,
+ {OPT_Imm|OPS_32|OPTM_Near|OPA_JmpRel|OPAP_JmpFar, 0, 0} },
+ { CPU_Any, 0, 0, 1, {0xE9, 0xEA, 0}, 0, 1,
+ {OPT_Imm|OPS_Any|OPTM_Near|OPA_JmpRel|OPAP_JmpFar, 0, 0} },
{ CPU_Any, 0, 16, 1, {0xFF, 0, 0}, 4, 1, {OPT_RM|OPS_16|OPA_EA, 0, 0} },
{ CPU_386|CPU_Not64, 0, 32, 1, {0xFF, 0, 0}, 4, 1,
{ CPU_Any, 0, 0, 1, {0xFF, 0, 0}, 4, 1,
{OPT_Mem|OPS_Any|OPTM_Near|OPA_EA, 0, 0} },
- /* TODO: Far Imm 16:16/32 */
+ { CPU_Any, 0, 16, 1, {0xEA, 0, 0}, 3, 1,
+ {OPT_Imm|OPS_16|OPTM_Far|OPA_JmpRel, 0, 0} },
+ { CPU_386, 0, 32, 1, {0xEA, 0, 0}, 3, 1,
+ {OPT_Imm|OPS_32|OPTM_Far|OPA_JmpRel, 0, 0} },
+ { CPU_Any, 0, 0, 1, {0xEA, 0, 0}, 3, 1,
+ {OPT_Imm|OPS_Any|OPTM_Far|OPA_JmpRel, 0, 0} },
{ CPU_Any, 0, 16, 1, {0xFF, 0, 0}, 5, 1,
{OPT_Mem|OPS_16|OPTM_Far|OPA_EA, 0, 0} },
op = yasm_ops_first(operands);
if (op->type != YASM_INSN__OPERAND_IMM)
yasm_internal_error(N_("invalid operand conversion"));
- d.target = yasm_expr_new(YASM_EXPR_SUB, yasm_expr_expr(op->data.val),
- yasm_expr_sym(yasm_symrec_define_label("$", cur_section, prev_bc,
- 0, lindex)), lindex);
- /* See if the user explicitly specified short/near. */
+ /* Far target needs to become "seg imm:imm". */
+ if ((jrinfo->operands[0] & OPTM_MASK) == OPTM_Far)
+ d.target = yasm_expr_new_tree(
+ yasm_expr_new_branch(YASM_EXPR_SEG, op->data.val, lindex),
+ YASM_EXPR_SEGOFF, yasm_expr_copy(op->data.val), lindex);
+ else
+ d.target = op->data.val;
+
+ /* Need to save jump origin for relative jumps. */
+ d.origin = yasm_symrec_define_label("$", cur_section, prev_bc, 0, lindex);
+
+ /* Initially assume no far opcode is available. */
+ d.far_op_len = 0;
+
+ /* See if the user explicitly specified short/near/far. */
switch ((int)(jrinfo->operands[0] & OPTM_MASK)) {
case OPTM_Short:
d.op_sel = JR_SHORT_FORCED;
case OPTM_Near:
d.op_sel = JR_NEAR_FORCED;
break;
+ case OPTM_Far:
+ d.op_sel = JR_FAR;
+ d.far_op_len = info->opcode_len;
+ d.far_op[0] = info->opcode[0];
+ d.far_op[1] = info->opcode[1];
+ d.far_op[2] = info->opcode[2];
+ break;
default:
d.op_sel = JR_NONE;
}
d.near_op[2] = info->opcode[2];
if (info->modifiers & MOD_Op1Add)
d.near_op[1] += (unsigned char)(mod_data & 0xFF);
+ if ((info->operands[0] & OPAP_MASK) == OPAP_JmpFar) {
+ d.far_op_len = 1;
+ d.far_op[0] = info->opcode[info->opcode_len];
+ }
break;
}
}