/* * x86 bytecode utility functions * * Copyright (C) 2001-2007 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 /*@unused@*/ RCSID("$Id: x86bc.c 2279 2010-01-19 07:57:43Z peter $"); #include #include "x86arch.h" /* Bytecode callback function prototypes */ static void x86_bc_insn_destroy(void *contents); static void x86_bc_insn_print(const void *contents, FILE *f, int indent_level); static int x86_bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); static int x86_bc_insn_expand(yasm_bytecode *bc, int span, long old_val, long new_val, /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres); static int x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, yasm_output_value_func output_value, /*@null@*/ yasm_output_reloc_func output_reloc); static void x86_bc_jmp_destroy(void *contents); static void x86_bc_jmp_print(const void *contents, FILE *f, int indent_level); static int x86_bc_jmp_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); static int x86_bc_jmp_expand(yasm_bytecode *bc, int span, long old_val, long new_val, /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres); static int x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, yasm_output_value_func output_value, /*@null@*/ yasm_output_reloc_func output_reloc); static void x86_bc_jmpfar_destroy(void *contents); static void x86_bc_jmpfar_print(const void *contents, FILE *f, int indent_level); static int x86_bc_jmpfar_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); static int x86_bc_jmpfar_tobytes (yasm_bytecode *bc, unsigned char **bufp, void *d, yasm_output_value_func output_value, /*@null@*/ yasm_output_reloc_func output_reloc); /* Bytecode callback structures */ static const yasm_bytecode_callback x86_bc_callback_insn = { x86_bc_insn_destroy, x86_bc_insn_print, yasm_bc_finalize_common, NULL, x86_bc_insn_calc_len, x86_bc_insn_expand, x86_bc_insn_tobytes, 0 }; static const yasm_bytecode_callback x86_bc_callback_jmp = { x86_bc_jmp_destroy, x86_bc_jmp_print, yasm_bc_finalize_common, NULL, x86_bc_jmp_calc_len, x86_bc_jmp_expand, x86_bc_jmp_tobytes, 0 }; static const yasm_bytecode_callback x86_bc_callback_jmpfar = { x86_bc_jmpfar_destroy, x86_bc_jmpfar_print, yasm_bc_finalize_common, NULL, x86_bc_jmpfar_calc_len, yasm_bc_expand_common, x86_bc_jmpfar_tobytes, 0 }; int yasm_x86__set_rex_from_reg(unsigned char *rex, unsigned char *low3, uintptr_t 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) { /* Check to make sure we can set it */ if (*rex == 0xff) { yasm_error_set(YASM_ERROR_TYPE, N_("cannot use A/B/C/DH with instruction needing REX")); 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) { yasm_error_set(YASM_ERROR_TYPE, N_("cannot use A/B/C/DH with instruction needing REX")); return 1; } *rex = 0xff; /* Flag so we can NEVER set it (see above) */ } } return 0; } void yasm_x86__bc_transform_insn(yasm_bytecode *bc, x86_insn *insn) { yasm_bc_transform(bc, &x86_bc_callback_insn, insn); } void yasm_x86__bc_transform_jmp(yasm_bytecode *bc, x86_jmp *jmp) { yasm_bc_transform(bc, &x86_bc_callback_jmp, jmp); } void yasm_x86__bc_transform_jmpfar(yasm_bytecode *bc, x86_jmpfar *jmpfar) { yasm_bc_transform(bc, &x86_bc_callback_jmpfar, jmpfar); } void yasm_x86__ea_init(x86_effaddr *x86_ea, unsigned int spare, yasm_bytecode *precbc) { if (yasm_value_finalize(&x86_ea->ea.disp, precbc)) yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("effective address too complex")); x86_ea->modrm &= 0xC7; /* zero spare/reg bits */ x86_ea->modrm |= (spare << 3) & 0x38; /* plug in provided bits */ } void yasm_x86__ea_set_disponly(x86_effaddr *x86_ea) { x86_ea->valid_modrm = 0; x86_ea->need_modrm = 0; x86_ea->valid_sib = 0; x86_ea->need_sib = 0; } static x86_effaddr * ea_create(void) { x86_effaddr *x86_ea = yasm_xmalloc(sizeof(x86_effaddr)); yasm_value_initialize(&x86_ea->ea.disp, NULL, 0); x86_ea->ea.need_nonzero_len = 0; x86_ea->ea.need_disp = 0; x86_ea->ea.nosplit = 0; x86_ea->ea.strong = 0; x86_ea->ea.segreg = 0; x86_ea->ea.pc_rel = 0; x86_ea->ea.not_pc_rel = 0; x86_ea->ea.data_len = 0; x86_ea->modrm = 0; x86_ea->valid_modrm = 0; x86_ea->need_modrm = 0; x86_ea->sib = 0; x86_ea->valid_sib = 0; x86_ea->need_sib = 0; return x86_ea; } x86_effaddr * yasm_x86__ea_create_reg(x86_effaddr *x86_ea, unsigned long reg, unsigned char *rex, unsigned int bits) { unsigned char rm; if (yasm_x86__set_rex_from_reg(rex, &rm, reg, bits, X86_REX_B)) return NULL; if (!x86_ea) x86_ea = ea_create(); x86_ea->modrm = 0xC0 | rm; /* Mod=11, R/M=Reg, Reg=0 */ x86_ea->valid_modrm = 1; x86_ea->need_modrm = 1; return x86_ea; } yasm_effaddr * yasm_x86__ea_create_expr(yasm_arch *arch, yasm_expr *e) { yasm_arch_x86 *arch_x86 = (yasm_arch_x86 *)arch; x86_effaddr *x86_ea; x86_ea = ea_create(); if (arch_x86->parser == X86_PARSER_GAS) { /* Need to change foo+rip into foo wrt rip (even in .intel_syntax mode). * Note this assumes a particular ordering coming from the parser * to work (it's not very smart)! */ if (e->op == YASM_EXPR_ADD && e->terms[0].type == YASM_EXPR_REG && e->terms[0].data.reg == X86_RIP) { /* replace register with 0 */ e->terms[0].type = YASM_EXPR_INT; e->terms[0].data.intn = yasm_intnum_create_uint(0); /* build new wrt expression */ e = yasm_expr_create(YASM_EXPR_WRT, yasm_expr_expr(e), yasm_expr_reg(X86_RIP), e->line); } } yasm_value_initialize(&x86_ea->ea.disp, e, 0); x86_ea->ea.need_disp = 1; x86_ea->need_modrm = 1; /* 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->ea.data_len = 0; return (yasm_effaddr *)x86_ea; } /*@-compmempass@*/ x86_effaddr * yasm_x86__ea_create_imm(x86_effaddr *x86_ea, yasm_expr *imm, unsigned int im_len) { if (!x86_ea) x86_ea = ea_create(); yasm_value_initialize(&x86_ea->ea.disp, imm, im_len); x86_ea->ea.need_disp = 1; return x86_ea; } /*@=compmempass@*/ void yasm_x86__bc_apply_prefixes(x86_common *common, unsigned char *rex, unsigned int def_opersize_64, unsigned int num_prefixes, uintptr_t *prefixes) { unsigned int i; int first = 1; for (i=0; ilockrep_pre != 0) yasm_warn_set(YASM_WARN_GENERAL, N_("multiple LOCK or REP prefixes, using leftmost")); common->lockrep_pre = (unsigned char)prefixes[i] & 0xff; break; case X86_ADDRSIZE: common->addrsize = (unsigned char)prefixes[i] & 0xff; break; case X86_OPERSIZE: common->opersize = (unsigned char)prefixes[i] & 0xff; if (common->mode_bits == 64 && common->opersize == 64 && def_opersize_64 != 64) { if (!rex) yasm_warn_set(YASM_WARN_GENERAL, N_("ignoring REX prefix on jump")); else if (*rex == 0xff) yasm_warn_set(YASM_WARN_GENERAL, N_("REX prefix not allowed on this instruction, ignoring")); else *rex = 0x48; } break; case X86_SEGREG: /* This is a hack.. we should really be putting this in the * the effective address! */ common->lockrep_pre = (unsigned char)prefixes[i] & 0xff; break; case X86_REX: if (!rex) yasm_warn_set(YASM_WARN_GENERAL, N_("ignoring REX prefix on jump")); else if (*rex == 0xff) yasm_warn_set(YASM_WARN_GENERAL, N_("REX prefix not allowed on this instruction, ignoring")); else { if (*rex != 0) { if (first) yasm_warn_set(YASM_WARN_GENERAL, N_("overriding generated REX prefix")); else yasm_warn_set(YASM_WARN_GENERAL, N_("multiple REX prefixes, using leftmost")); } /* Here we assume that we can't get this prefix in non * 64 bit mode due to checks in parse_check_prefix(). */ common->mode_bits = 64; *rex = (unsigned char)prefixes[i] & 0xff; } first = 0; break; } } } static void x86_bc_insn_destroy(void *contents) { x86_insn *insn = (x86_insn *)contents; if (insn->x86_ea) yasm_x86__ea_destroy((yasm_effaddr *)insn->x86_ea); if (insn->imm) { yasm_value_delete(insn->imm); yasm_xfree(insn->imm); } yasm_xfree(contents); } static void x86_bc_jmp_destroy(void *contents) { x86_jmp *jmp = (x86_jmp *)contents; yasm_value_delete(&jmp->target); yasm_xfree(contents); } static void x86_bc_jmpfar_destroy(void *contents) { x86_jmpfar *jmpfar = (x86_jmpfar *)contents; yasm_value_delete(&jmpfar->segment); yasm_value_delete(&jmpfar->offset); yasm_xfree(contents); } void yasm_x86__ea_destroy(yasm_effaddr *ea) { yasm_value_delete(&ea->disp); yasm_xfree(ea); } void yasm_x86__ea_print(const yasm_effaddr *ea, FILE *f, int indent_level) { const x86_effaddr *x86_ea = (const x86_effaddr *)ea; fprintf(f, "%*sDisp:\n", indent_level, ""); yasm_value_print(&ea->disp, f, indent_level+1); fprintf(f, "%*sNoSplit=%u\n", indent_level, "", (unsigned int)ea->nosplit); fprintf(f, "%*sSegmentOv=%02x\n", indent_level, "", (unsigned int)x86_ea->ea.segreg); fprintf(f, "%*sModRM=%03o ValidRM=%u NeedRM=%u\n", indent_level, "", (unsigned int)x86_ea->modrm, (unsigned int)x86_ea->valid_modrm, (unsigned int)x86_ea->need_modrm); fprintf(f, "%*sSIB=%03o ValidSIB=%u NeedSIB=%u\n", indent_level, "", (unsigned int)x86_ea->sib, (unsigned int)x86_ea->valid_sib, (unsigned int)x86_ea->need_sib); } static void x86_common_print(const x86_common *common, FILE *f, int indent_level) { fprintf(f, "%*sAddrSize=%u OperSize=%u LockRepPre=%02x BITS=%u\n", indent_level, "", (unsigned int)common->addrsize, (unsigned int)common->opersize, (unsigned int)common->lockrep_pre, (unsigned int)common->mode_bits); } static void x86_opcode_print(const x86_opcode *opcode, FILE *f, int indent_level) { fprintf(f, "%*sOpcode: %02x %02x %02x OpLen=%u\n", indent_level, "", (unsigned int)opcode->opcode[0], (unsigned int)opcode->opcode[1], (unsigned int)opcode->opcode[2], (unsigned int)opcode->len); } static void x86_bc_insn_print(const void *contents, FILE *f, int indent_level) { const x86_insn *insn = (const x86_insn *)contents; fprintf(f, "%*s_Instruction_\n", indent_level, ""); fprintf(f, "%*sEffective Address:", indent_level, ""); if (insn->x86_ea) { fprintf(f, "\n"); yasm_x86__ea_print((yasm_effaddr *)insn->x86_ea, f, indent_level+1); } else fprintf(f, " (nil)\n"); fprintf(f, "%*sImmediate Value:", indent_level, ""); if (!insn->imm) fprintf(f, " (nil)\n"); else { indent_level++; fprintf(f, "\n"); yasm_value_print(insn->imm, f, indent_level); indent_level--; } x86_opcode_print(&insn->opcode, f, indent_level); x86_common_print(&insn->common, f, indent_level); fprintf(f, "%*sSpPre=%02x REX=%03o PostOp=%u\n", indent_level, "", (unsigned int)insn->special_prefix, (unsigned int)insn->rex, (unsigned int)insn->postop); } static void x86_bc_jmp_print(const void *contents, FILE *f, int indent_level) { const x86_jmp *jmp = (const x86_jmp *)contents; fprintf(f, "%*s_Jump_\n", indent_level, ""); fprintf(f, "%*sTarget:\n", indent_level, ""); yasm_value_print(&jmp->target, f, indent_level+1); /* FIXME fprintf(f, "%*sOrigin=\n", indent_level, ""); yasm_symrec_print(jmp->origin, f, indent_level+1); */ fprintf(f, "\n%*sShort Form:\n", indent_level, ""); if (jmp->shortop.len == 0) fprintf(f, "%*sNone\n", indent_level+1, ""); else x86_opcode_print(&jmp->shortop, f, indent_level+1); fprintf(f, "%*sNear Form:\n", indent_level, ""); if (jmp->nearop.len == 0) fprintf(f, "%*sNone\n", indent_level+1, ""); else x86_opcode_print(&jmp->nearop, f, indent_level+1); fprintf(f, "%*sOpSel=", indent_level, ""); switch (jmp->op_sel) { case JMP_NONE: fprintf(f, "None"); break; case JMP_SHORT: fprintf(f, "Short"); break; case JMP_NEAR: fprintf(f, "Near"); break; case JMP_SHORT_FORCED: fprintf(f, "Forced Short"); break; case JMP_NEAR_FORCED: fprintf(f, "Forced Near"); break; default: fprintf(f, "UNKNOWN!!"); break; } x86_common_print(&jmp->common, f, indent_level); } static void x86_bc_jmpfar_print(const void *contents, FILE *f, int indent_level) { const x86_jmpfar *jmpfar = (const x86_jmpfar *)contents; fprintf(f, "%*s_Far_Jump_\n", indent_level, ""); fprintf(f, "%*sSegment:\n", indent_level, ""); yasm_value_print(&jmpfar->segment, f, indent_level+1); fprintf(f, "%*sOffset:\n", indent_level, ""); yasm_value_print(&jmpfar->offset, f, indent_level+1); x86_opcode_print(&jmpfar->opcode, f, indent_level); x86_common_print(&jmpfar->common, f, indent_level); } static unsigned int x86_common_calc_len(const x86_common *common) { unsigned int len = 0; if (common->addrsize != 0 && common->addrsize != common->mode_bits) len++; if (common->opersize != 0 && ((common->mode_bits != 64 && common->opersize != common->mode_bits) || (common->mode_bits == 64 && common->opersize == 16))) len++; if (common->lockrep_pre != 0) len++; return len; } static int x86_bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data) { x86_insn *insn = (x86_insn *)bc->contents; x86_effaddr *x86_ea = insn->x86_ea; yasm_value *imm = insn->imm; if (x86_ea) { /* 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 (yasm_x86__expr_checkea(x86_ea, &insn->common.addrsize, insn->common.mode_bits, insn->postop == X86_POSTOP_ADDRESS16, &insn->rex, bc)) /* failed, don't bother checking rest of insn */ return -1; if (x86_ea->ea.disp.size == 0 && x86_ea->ea.need_nonzero_len) { /* Handle unknown case, default to byte-sized and set as * critical expression. */ x86_ea->ea.disp.size = 8; add_span(add_span_data, bc, 1, &x86_ea->ea.disp, -128, 127); } bc->len += x86_ea->ea.disp.size/8; /* Handle address16 postop case */ if (insn->postop == X86_POSTOP_ADDRESS16) insn->common.addrsize = 0; /* Compute length of ea and add to total */ bc->len += x86_ea->need_modrm + (x86_ea->need_sib ? 1:0); bc->len += (x86_ea->ea.segreg != 0) ? 1 : 0; } if (imm) { unsigned int immlen = imm->size; /* TODO: check imm->len vs. sized len from expr? */ /* Handle signext_imm8 postop special-casing */ if (insn->postop == X86_POSTOP_SIGNEXT_IMM8) { /*@null@*/ /*@only@*/ yasm_intnum *num; num = yasm_value_get_intnum(imm, NULL, 0); if (!num) { /* Unknown; default to byte form and set as critical * expression. */ immlen = 8; add_span(add_span_data, bc, 2, imm, -128, 127); } else { if (yasm_intnum_in_range(num, -128, 127)) { /* We can use the sign-extended byte form: shorten * the immediate length to 1 and make the byte form * permanent. */ imm->size = 8; imm->sign = 1; immlen = 8; } else { /* We can't. Copy over the word-sized opcode. */ insn->opcode.opcode[0] = insn->opcode.opcode[insn->opcode.len]; insn->opcode.len = 1; } insn->postop = X86_POSTOP_NONE; yasm_intnum_destroy(num); } } bc->len += immlen/8; } /* VEX and XOP prefixes never have REX (it's embedded in the opcode). * For VEX, we can come into this function with the three byte form, * so we need to see if we can optimize to the two byte form. * We can't do it earlier, as we don't know all of the REX byte until now. */ if (insn->special_prefix == 0xC4) { /* See if we can shorten the VEX prefix to its two byte form. * In order to do this, REX.X, REX.B, and REX.W/VEX.W must all be 0, * and the VEX mmmmm field must be 1. */ if ((insn->opcode.opcode[0] & 0x1F) == 1 && (insn->opcode.opcode[1] & 0x80) == 0 && (insn->rex == 0xff || (insn->rex & 0x0B) == 0)) { insn->opcode.opcode[0] = insn->opcode.opcode[1]; insn->opcode.opcode[1] = insn->opcode.opcode[2]; insn->opcode.opcode[2] = 0; /* sanity */ insn->opcode.len = 2; insn->special_prefix = 0xC5; /* mark as two-byte VEX */ } } else if (insn->rex != 0xff && insn->rex != 0 && insn->special_prefix != 0xC5 && insn->special_prefix != 0x8F) bc->len++; bc->len += insn->opcode.len; bc->len += x86_common_calc_len(&insn->common); bc->len += (insn->special_prefix != 0) ? 1:0; return 0; } static int x86_bc_insn_expand(yasm_bytecode *bc, int span, long old_val, long new_val, /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) { x86_insn *insn = (x86_insn *)bc->contents; x86_effaddr *x86_ea = insn->x86_ea; yasm_effaddr *ea = &x86_ea->ea; yasm_value *imm = insn->imm; if (ea && span == 1) { /* Change displacement length into word-sized */ if (ea->disp.size == 8) { ea->disp.size = (insn->common.addrsize == 16) ? 16 : 32; x86_ea->modrm &= ~0300; x86_ea->modrm |= 0200; bc->len--; bc->len += ea->disp.size/8; } } if (imm && span == 2) { if (insn->postop == X86_POSTOP_SIGNEXT_IMM8) { /* Update bc->len for new opcode and immediate size */ bc->len -= insn->opcode.len; bc->len += imm->size/8; /* Change to the word-sized opcode */ insn->opcode.opcode[0] = insn->opcode.opcode[insn->opcode.len]; insn->opcode.len = 1; insn->postop = X86_POSTOP_NONE; } } return 0; } static int x86_bc_jmp_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data) { x86_jmp *jmp = (x86_jmp *)bc->contents; yasm_bytecode *target_prevbc; unsigned char opersize; /* As opersize may be 0, figure out its "real" value. */ opersize = (jmp->common.opersize == 0) ? jmp->common.mode_bits : jmp->common.opersize; bc->len += x86_common_calc_len(&jmp->common); if (jmp->op_sel == JMP_NEAR_FORCED || jmp->shortop.len == 0) { if (jmp->nearop.len == 0) { yasm_error_set(YASM_ERROR_TYPE, N_("near jump does not exist")); return -1; } /* Near jump, no spans needed */ if (jmp->shortop.len == 0) jmp->op_sel = JMP_NEAR; bc->len += jmp->nearop.len; bc->len += (opersize == 16) ? 2 : 4; return 0; } if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) { if (jmp->shortop.len == 0) { yasm_error_set(YASM_ERROR_TYPE, N_("short jump does not exist")); return -1; } /* We want to be sure to error if we exceed short length, so * put it in as a dependent expression (falling through). */ } if (jmp->target.rel && (!yasm_symrec_get_label(jmp->target.rel, &target_prevbc) || target_prevbc->section != bc->section)) { /* External or out of segment, so we can't check distance. * Allowing short jumps depends on the objfmt supporting * 8-bit relocs. While most don't, some might, so allow it here. * Otherwise default to word-sized. * The objfmt will error if not supported. */ if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) { if (jmp->op_sel == JMP_NONE) jmp->op_sel = JMP_SHORT; bc->len += jmp->shortop.len + 1; } else { jmp->op_sel = JMP_NEAR; bc->len += jmp->nearop.len; bc->len += (opersize == 16) ? 2 : 4; } return 0; } /* Default to short jump and generate span */ if (jmp->op_sel == JMP_NONE) jmp->op_sel = JMP_SHORT; bc->len += jmp->shortop.len + 1; add_span(add_span_data, bc, 1, &jmp->target, -128+(long)bc->len, 127+(long)bc->len); return 0; } static int x86_bc_jmp_expand(yasm_bytecode *bc, int span, long old_val, long new_val, /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) { x86_jmp *jmp = (x86_jmp *)bc->contents; unsigned char opersize; if (span != 1) yasm_internal_error(N_("unrecognized span id")); /* As opersize may be 0, figure out its "real" value. */ opersize = (jmp->common.opersize == 0) ? jmp->common.mode_bits : jmp->common.opersize; if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) { yasm_error_set(YASM_ERROR_VALUE, N_("short jump out of range")); return -1; } if (jmp->op_sel == JMP_NEAR) yasm_internal_error(N_("trying to expand an already-near jump")); /* Upgrade to a near jump */ jmp->op_sel = JMP_NEAR; bc->len -= jmp->shortop.len + 1; bc->len += jmp->nearop.len; bc->len += (opersize == 16) ? 2 : 4; return 0; } static int x86_bc_jmpfar_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data) { x86_jmpfar *jmpfar = (x86_jmpfar *)bc->contents; unsigned char opersize; opersize = (jmpfar->common.opersize == 0) ? jmpfar->common.mode_bits : jmpfar->common.opersize; bc->len += jmpfar->opcode.len; bc->len += 2; /* segment */ bc->len += (opersize == 16) ? 2 : 4; bc->len += x86_common_calc_len(&jmpfar->common); return 0; } static void x86_common_tobytes(const x86_common *common, unsigned char **bufp, unsigned int segreg) { if (segreg != 0) YASM_WRITE_8(*bufp, (unsigned char)segreg); if (common->addrsize != 0 && common->addrsize != common->mode_bits) YASM_WRITE_8(*bufp, 0x67); if (common->opersize != 0 && ((common->mode_bits != 64 && common->opersize != common->mode_bits) || (common->mode_bits == 64 && common->opersize == 16))) YASM_WRITE_8(*bufp, 0x66); if (common->lockrep_pre != 0) YASM_WRITE_8(*bufp, common->lockrep_pre); } static void x86_opcode_tobytes(const x86_opcode *opcode, unsigned char **bufp) { unsigned int i; for (i=0; ilen; i++) YASM_WRITE_8(*bufp, opcode->opcode[i]); } static int x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, yasm_output_value_func output_value, /*@unused@*/ yasm_output_reloc_func output_reloc) { x86_insn *insn = (x86_insn *)bc->contents; /*@null@*/ x86_effaddr *x86_ea = (x86_effaddr *)insn->x86_ea; yasm_value *imm = insn->imm; unsigned char *bufp_orig = *bufp; /* Prefixes */ x86_common_tobytes(&insn->common, bufp, x86_ea ? (unsigned int)(x86_ea->ea.segreg>>8) : 0); if (insn->special_prefix != 0) YASM_WRITE_8(*bufp, insn->special_prefix); if (insn->special_prefix == 0xC4 || insn->special_prefix == 0x8F) { /* 3-byte VEX/XOP; merge in 1s complement of REX.R, REX.X, REX.B */ insn->opcode.opcode[0] &= 0x1F; if (insn->rex != 0xff) insn->opcode.opcode[0] |= ((~insn->rex) & 0x07) << 5; /* merge REX.W via ORing; there should never be a case in which REX.W * is important when VEX.W is already set by the instruction. */ if (insn->rex != 0xff && (insn->rex & 0x8) != 0) insn->opcode.opcode[1] |= 0x80; } else if (insn->special_prefix == 0xC5) { /* 2-byte VEX; merge in 1s complement of REX.R */ insn->opcode.opcode[0] &= 0x7F; if (insn->rex != 0xff && (insn->rex & 0x4) == 0) insn->opcode.opcode[0] |= 0x80; /* No other REX bits should be set */ if (insn->rex != 0xff && (insn->rex & 0xB) != 0) yasm_internal_error(N_("x86: REX.WXB set, but 2-byte VEX")); } else if (insn->rex != 0xff && insn->rex != 0) { if (insn->common.mode_bits != 64) yasm_internal_error(N_("x86: got a REX prefix in non-64-bit mode")); YASM_WRITE_8(*bufp, insn->rex); } /* Opcode */ x86_opcode_tobytes(&insn->opcode, bufp); /* Effective address: ModR/M (if required), SIB (if required), and * displacement (if required). */ if (x86_ea) { if (x86_ea->need_modrm) { if (!x86_ea->valid_modrm) yasm_internal_error(N_("invalid Mod/RM in x86 tobytes_insn")); YASM_WRITE_8(*bufp, x86_ea->modrm); } if (x86_ea->need_sib) { if (!x86_ea->valid_sib) yasm_internal_error(N_("invalid SIB in x86 tobytes_insn")); YASM_WRITE_8(*bufp, x86_ea->sib); } if (x86_ea->ea.need_disp) { unsigned int disp_len = x86_ea->ea.disp.size/8; if (x86_ea->ea.disp.ip_rel) { /* Adjust relative displacement to end of bytecode */ /*@only@*/ yasm_intnum *delta; delta = yasm_intnum_create_int(-(long)bc->len); if (!x86_ea->ea.disp.abs) x86_ea->ea.disp.abs = yasm_expr_create_ident(yasm_expr_int(delta), bc->line); else x86_ea->ea.disp.abs = yasm_expr_create(YASM_EXPR_ADD, yasm_expr_expr(x86_ea->ea.disp.abs), yasm_expr_int(delta), bc->line); } if (output_value(&x86_ea->ea.disp, *bufp, disp_len, (unsigned long)(*bufp-bufp_orig), bc, 1, d)) return 1; *bufp += disp_len; } } /* Immediate (if required) */ if (imm) { unsigned int imm_len; if (insn->postop == X86_POSTOP_SIGNEXT_IMM8) { /* If we got here with this postop still set, we need to force * imm size to 8 here. */ imm->size = 8; imm->sign = 1; imm_len = 1; } else imm_len = imm->size/8; if (output_value(imm, *bufp, imm_len, (unsigned long)(*bufp-bufp_orig), bc, 1, d)) return 1; *bufp += imm_len; } return 0; } static int x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, yasm_output_value_func output_value, /*@unused@*/ yasm_output_reloc_func output_reloc) { x86_jmp *jmp = (x86_jmp *)bc->contents; unsigned char opersize; unsigned int i; unsigned char *bufp_orig = *bufp; /*@only@*/ yasm_intnum *delta; /* Prefixes */ x86_common_tobytes(&jmp->common, bufp, 0); /* As opersize may be 0, figure out its "real" value. */ opersize = (jmp->common.opersize == 0) ? jmp->common.mode_bits : jmp->common.opersize; /* Check here again to see if forms are actually legal. */ switch (jmp->op_sel) { case JMP_SHORT_FORCED: case JMP_SHORT: /* 1 byte relative displacement */ if (jmp->shortop.len == 0) yasm_internal_error(N_("short jump does not exist")); /* Opcode */ x86_opcode_tobytes(&jmp->shortop, bufp); /* Adjust relative displacement to end of bytecode */ delta = yasm_intnum_create_int(-(long)bc->len); if (!jmp->target.abs) jmp->target.abs = yasm_expr_create_ident(yasm_expr_int(delta), bc->line); else jmp->target.abs = yasm_expr_create(YASM_EXPR_ADD, yasm_expr_expr(jmp->target.abs), yasm_expr_int(delta), bc->line); jmp->target.size = 8; jmp->target.sign = 1; if (output_value(&jmp->target, *bufp, 1, (unsigned long)(*bufp-bufp_orig), bc, 1, d)) return 1; *bufp += 1; break; case JMP_NEAR_FORCED: case JMP_NEAR: /* 2/4 byte relative displacement (depending on operand size) */ if (jmp->nearop.len == 0) { yasm_error_set(YASM_ERROR_TYPE, N_("near jump does not exist")); return 1; } /* Opcode */ x86_opcode_tobytes(&jmp->nearop, bufp); i = (opersize == 16) ? 2 : 4; /* Adjust relative displacement to end of bytecode */ delta = yasm_intnum_create_int(-(long)bc->len); if (!jmp->target.abs) jmp->target.abs = yasm_expr_create_ident(yasm_expr_int(delta), bc->line); else jmp->target.abs = yasm_expr_create(YASM_EXPR_ADD, yasm_expr_expr(jmp->target.abs), yasm_expr_int(delta), bc->line); jmp->target.size = i*8; jmp->target.sign = 1; if (output_value(&jmp->target, *bufp, i, (unsigned long)(*bufp-bufp_orig), bc, 1, d)) return 1; *bufp += i; break; case JMP_NONE: yasm_internal_error(N_("jump op_sel cannot be JMP_NONE in tobytes")); default: yasm_internal_error(N_("unrecognized relative jump op_sel")); } return 0; } static int x86_bc_jmpfar_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, yasm_output_value_func output_value, /*@unused@*/ yasm_output_reloc_func output_reloc) { x86_jmpfar *jmpfar = (x86_jmpfar *)bc->contents; unsigned int i; unsigned char *bufp_orig = *bufp; unsigned char opersize; x86_common_tobytes(&jmpfar->common, bufp, 0); x86_opcode_tobytes(&jmpfar->opcode, bufp); /* As opersize may be 0, figure out its "real" value. */ opersize = (jmpfar->common.opersize == 0) ? jmpfar->common.mode_bits : jmpfar->common.opersize; /* Absolute displacement: segment and offset */ i = (opersize == 16) ? 2 : 4; jmpfar->offset.size = i*8; if (output_value(&jmpfar->offset, *bufp, i, (unsigned long)(*bufp-bufp_orig), bc, 1, d)) return 1; *bufp += i; jmpfar->segment.size = 16; if (output_value(&jmpfar->segment, *bufp, 2, (unsigned long)(*bufp-bufp_orig), bc, 1, d)) return 1; *bufp += 2; return 0; } int yasm_x86__intnum_tobytes(yasm_arch *arch, const yasm_intnum *intn, unsigned char *buf, size_t destsize, size_t valsize, int shift, const yasm_bytecode *bc, int warn) { /* Write value out. */ yasm_intnum_get_sized(intn, buf, destsize, valsize, shift, 0, warn); return 0; }