From: Peter Johnson Date: Tue, 27 May 2003 04:13:16 +0000 (-0000) Subject: Add support for FAR call/jmp. Because of the jmp label, label equ seg:off X-Git-Tag: v0.2.2~3^2~1 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=13cbbfcb4ad313d0ae2939462af3b8d479a4836a;p=yasm Add support for FAR call/jmp. Because of the jmp label, label equ seg:off problem, adding this required adding some fields to x86_jmprel (now a misnomer, as FAR jumps are absolute) to save the far opcode, and additional support in libyasm's yasm_expr_* to properly handle the YASM_EXPR_SEGOFF operator. svn path=/trunk/yasm/; revision=954 --- diff --git a/libyasm/bytecode.h b/libyasm/bytecode.h index c7f75429..607df29e 100644 --- a/libyasm/bytecode.h +++ b/libyasm/bytecode.h @@ -2,7 +2,7 @@ * \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 * @@ -257,6 +257,8 @@ yasm_bc_resolve_flags yasm_bc_resolve(yasm_bytecode *bc, int save, * \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, diff --git a/libyasm/coretype.h b/libyasm/coretype.h index d32f78a8..94a52f4e 100644 --- a/libyasm/coretype.h +++ b/libyasm/coretype.h @@ -2,7 +2,7 @@ * \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 * @@ -85,6 +85,7 @@ typedef struct yasm_valparamhead yasm_valparamhead; /** 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 (*). */ @@ -108,11 +109,11 @@ typedef enum { 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. diff --git a/libyasm/expr.c b/libyasm/expr.c index 2963c637..138cc41a 100644 --- a/libyasm/expr.c +++ b/libyasm/expr.c @@ -519,6 +519,11 @@ expr_level_op(/*@returned@*/ /*@only@*/ yasm_expr *e, int fold_const, 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; inumterms; i++) { @@ -880,6 +885,12 @@ yasm_expr_delete(yasm_expr *e) } /*@=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) { @@ -1017,6 +1028,35 @@ yasm_expr_extract_symrec(yasm_expr **ep, yasm_calc_bc_dist_func calc_bc_dist) 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) @@ -1168,6 +1208,9 @@ yasm_expr_print(FILE *f, const yasm_expr *e) case YASM_EXPR_IDENT: opstr[0] = 0; break; + default: + strcpy(opstr, " !UNK! "); + break; } for (i=0; inumterms; i++) { switch (e->terms[i].type) { diff --git a/libyasm/expr.h b/libyasm/expr.h index b0c639a1..786a2556 100644 --- a/libyasm/expr.h +++ b/libyasm/expr.h @@ -2,7 +2,7 @@ * \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 * @@ -117,6 +117,14 @@ yasm_expr *yasm_expr_copy(const yasm_expr *e); */ 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 @@ -172,6 +180,15 @@ SLIST_HEAD(yasm__exprhead, yasm__exprentry); /*@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 diff --git a/modules/arch/x86/x86arch.h b/modules/arch/x86/x86arch.h index d96dbef6..99a1694b 100644 --- a/modules/arch/x86/x86arch.h +++ b/modules/arch/x86/x86arch.h @@ -69,7 +69,8 @@ typedef enum { 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 { @@ -135,11 +136,14 @@ yasm_bytecode *yasm_x86__bc_new_insn(x86_new_insn_data *d); 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; diff --git a/modules/arch/x86/x86bc.c b/modules/arch/x86/x86bc.c index a40312ba..2c5cfc94 100644 --- a/modules/arch/x86/x86bc.c +++ b/modules/arch/x86/x86bc.c @@ -25,7 +25,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -/*@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 @@ -98,11 +98,12 @@ typedef struct x86_jmprel { 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 */ @@ -192,6 +193,7 @@ yasm_x86__bc_new_jmprel(x86_new_jmprel_data *d) 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)) @@ -211,6 +213,11 @@ yasm_x86__bc_new_jmprel(x86_new_jmprel_data *d) 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; @@ -539,6 +546,9 @@ yasm_x86__bc_print(FILE *f, int indent_level, const yasm_bytecode *bc) case JR_NEAR_FORCED: fprintf(f, "Forced Near"); break; + case JR_FAR: + fprintf(f, "Far"); + break; default: fprintf(f, "UNKNOWN!!"); break; @@ -677,7 +687,7 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, /*@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 : @@ -689,9 +699,11 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, 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, @@ -719,7 +731,7 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, 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")); @@ -728,12 +740,26 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, } 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); @@ -742,12 +768,12 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, 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 { @@ -762,7 +788,7 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, 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 @@ -772,7 +798,7 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, 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, @@ -780,28 +806,43 @@ x86_bc_resolve_jmprel(x86_jmprel *jmprel, unsigned long *len, int save, 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; @@ -933,6 +974,7 @@ x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp, unsigned char opersize; unsigned int i; unsigned char *bufp_orig = *bufp; + /*@null@*/ yasm_expr *targetseg; /* Prefixes */ if (jmprel->lockrep_pre != 0) @@ -960,6 +1002,9 @@ x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp, 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; @@ -977,11 +1022,39 @@ x86_bc_tobytes_jmprel(x86_jmprel *jmprel, unsigned char **bufp, 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; ifarop.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")); } diff --git a/modules/arch/x86/x86id.re b/modules/arch/x86/x86id.re index cd10684a..c5785cab 100644 --- a/modules/arch/x86/x86id.re +++ b/modules/arch/x86/x86id.re @@ -25,7 +25,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ #include -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 @@ -166,6 +166,7 @@ static unsigned long cpu_enabled = ~CPU_Any; * 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 @@ -226,6 +227,7 @@ static unsigned long cpu_enabled = ~CPU_Any; #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 { @@ -857,12 +859,12 @@ static const x86_insn_info call_insn[] = { { 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, @@ -879,7 +881,12 @@ static const x86_insn_info call_insn[] = { { 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} }, @@ -895,12 +902,12 @@ static const x86_insn_info jmp_insn[] = { { 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, @@ -917,7 +924,12 @@ static const x86_insn_info jmp_insn[] = { { 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} }, @@ -1529,11 +1541,22 @@ x86_new_jmprel(const unsigned long data[4], int num_operands, 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; @@ -1541,6 +1564,13 @@ x86_new_jmprel(const unsigned long data[4], int num_operands, 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; } @@ -1603,6 +1633,10 @@ x86_new_jmprel(const unsigned long data[4], int num_operands, 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; } }