| /* |
| * 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 <util.h> |
| |
| #include <libyasm.h> |
| |
| #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, |
| unsigned char *bufstart, |
| 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, |
| unsigned char *bufstart, |
| 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, unsigned char *bufstart, 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->vsib_mode = 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; i<num_prefixes; i++) { |
| switch ((x86_parse_insn_prefix)(prefixes[i] & 0xff00)) { |
| case X86_LOCKREP: |
| if (common->lockrep_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, "%*sVSIBMode=%u\n", indent_level, "", |
| (unsigned int)x86_ea->vsib_mode); |
| 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; i<opcode->len; i++) |
| YASM_WRITE_8(*bufp, opcode->opcode[i]); |
| } |
| |
| static int |
| x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, |
| unsigned char *bufstart, 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; |
| |
| /* 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-bufstart), 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-bufstart), |
| bc, 1, d)) |
| return 1; |
| *bufp += imm_len; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp, |
| unsigned char *bufstart, 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; |
| /*@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-bufstart), 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-bufstart), 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, |
| unsigned char *bufstart, 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 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-bufstart), bc, 1, d)) |
| return 1; |
| *bufp += i; |
| jmpfar->segment.size = 16; |
| if (output_value(&jmpfar->segment, *bufp, 2, |
| (unsigned long)(*bufp-bufstart), 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; |
| } |