| /* |
| * Insn bytecode |
| * |
| * Copyright (C) 2005-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. |
| */ |
| #define YASM_LIB_INTERNAL |
| #include "util.h" |
| /*@unused@*/ RCSID("$Id$"); |
| |
| #include "libyasm-stdint.h" |
| #include "coretype.h" |
| |
| #include "errwarn.h" |
| #include "expr.h" |
| #include "value.h" |
| |
| #include "bytecode.h" |
| #include "arch.h" |
| |
| #include "bc-int.h" |
| |
| |
| typedef struct bytecode_insn { |
| /*@dependent@*/ yasm_arch *arch; |
| uintptr_t insn_data[4]; |
| |
| int num_operands; |
| /*@null@*/ yasm_insn_operands operands; |
| |
| /* array of 4-element prefix_data arrays */ |
| int num_prefixes; |
| /*@null@*/ uintptr_t **prefixes; |
| |
| /* array of segment prefixes */ |
| int num_segregs; |
| /*@null@*/ uintptr_t *segregs; |
| } bytecode_insn; |
| |
| static void bc_insn_destroy(void *contents); |
| static void bc_insn_print(const void *contents, FILE *f, int indent_level); |
| static void bc_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); |
| static int bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, |
| void *add_span_data); |
| static int 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 const yasm_bytecode_callback bc_insn_callback = { |
| bc_insn_destroy, |
| bc_insn_print, |
| bc_insn_finalize, |
| bc_insn_calc_len, |
| yasm_bc_expand_common, |
| bc_insn_tobytes, |
| 0 |
| }; |
| |
| |
| const yasm_expr * |
| yasm_ea_get_disp(const yasm_effaddr *ea) |
| { |
| return ea->disp.abs; |
| } |
| |
| void |
| yasm_ea_set_len(yasm_effaddr *ptr, unsigned int len) |
| { |
| if (!ptr) |
| return; |
| |
| /* Currently don't warn if length truncated, as this is called only from |
| * an explicit override, where we expect the user knows what they're doing. |
| */ |
| |
| ptr->disp.size = (unsigned char)len; |
| } |
| |
| void |
| yasm_ea_set_nosplit(yasm_effaddr *ptr, unsigned int nosplit) |
| { |
| if (!ptr) |
| return; |
| |
| ptr->nosplit = (unsigned char)nosplit; |
| } |
| |
| void |
| yasm_ea_set_strong(yasm_effaddr *ptr, unsigned int strong) |
| { |
| if (!ptr) |
| return; |
| |
| ptr->strong = (unsigned char)strong; |
| } |
| |
| void |
| yasm_ea_set_segreg(yasm_effaddr *ea, uintptr_t segreg) |
| { |
| if (!ea) |
| return; |
| |
| if (segreg != 0 && ea->segreg != 0) |
| yasm_warn_set(YASM_WARN_GENERAL, |
| N_("multiple segment overrides, using leftmost")); |
| |
| ea->segreg = segreg; |
| } |
| |
| /*@-nullstate@*/ |
| void |
| yasm_ea_destroy(yasm_effaddr *ea) |
| { |
| ea->callback->destroy(ea); |
| yasm_value_delete(&ea->disp); |
| yasm_xfree(ea); |
| } |
| /*@=nullstate@*/ |
| |
| /*@-nullstate@*/ |
| void |
| yasm_ea_print(const yasm_effaddr *ea, FILE *f, int indent_level) |
| { |
| 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); |
| ea->callback->print(ea, f, indent_level); |
| } |
| /*@=nullstate@*/ |
| |
| static void |
| bc_insn_destroy(void *contents) |
| { |
| bytecode_insn *insn = (bytecode_insn *)contents; |
| if (insn->num_operands > 0) |
| yasm_ops_delete(&insn->operands, 0); |
| if (insn->num_prefixes > 0) { |
| int i; |
| for (i=0; i<insn->num_prefixes; i++) |
| yasm_xfree(insn->prefixes[i]); |
| yasm_xfree(insn->prefixes); |
| } |
| if (insn->num_segregs > 0) |
| yasm_xfree(insn->segregs); |
| yasm_xfree(contents); |
| } |
| |
| static void |
| bc_insn_print(const void *contents, FILE *f, int indent_level) |
| { |
| } |
| |
| static void |
| bc_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) |
| { |
| bytecode_insn *insn = (bytecode_insn *)bc->contents; |
| int i; |
| yasm_insn_operand *op; |
| yasm_error_class eclass; |
| char *str, *xrefstr; |
| unsigned long xrefline; |
| |
| /* Simplify the operands' expressions first. */ |
| for (i = 0, op = yasm_ops_first(&insn->operands); |
| op && i<insn->num_operands; op = yasm_operand_next(op), i++) { |
| /* Check operand type */ |
| switch (op->type) { |
| case YASM_INSN__OPERAND_MEMORY: |
| /* Don't get over-ambitious here; some archs' memory expr |
| * parser are sensitive to the presence of *1, etc, so don't |
| * simplify reg*1 identities. |
| */ |
| if (op->data.ea) |
| op->data.ea->disp.abs = |
| yasm_expr__level_tree(op->data.ea->disp.abs, 1, 1, 0, |
| 0, NULL, NULL); |
| if (yasm_error_occurred()) { |
| /* Add a pointer to where it was used to the error */ |
| yasm_error_fetch(&eclass, &str, &xrefline, &xrefstr); |
| if (xrefstr) { |
| yasm_error_set_xref(xrefline, "%s", xrefstr); |
| yasm_xfree(xrefstr); |
| } |
| if (str) { |
| yasm_error_set(eclass, "%s in memory expression", str); |
| yasm_xfree(str); |
| } |
| return; |
| } |
| break; |
| case YASM_INSN__OPERAND_IMM: |
| op->data.val = |
| yasm_expr__level_tree(op->data.val, 1, 1, 1, 0, NULL, |
| NULL); |
| if (yasm_error_occurred()) { |
| /* Add a pointer to where it was used to the error */ |
| yasm_error_fetch(&eclass, &str, &xrefline, &xrefstr); |
| if (xrefstr) { |
| yasm_error_set_xref(xrefline, "%s", xrefstr); |
| yasm_xfree(xrefstr); |
| } |
| if (str) { |
| yasm_error_set(eclass, "%s in immediate expression", |
| str); |
| yasm_xfree(str); |
| } |
| return; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| yasm_arch_finalize_insn(insn->arch, bc, prev_bc, insn->insn_data, |
| insn->num_operands, &insn->operands, |
| insn->num_prefixes, insn->prefixes, |
| insn->num_segregs, insn->segregs); |
| } |
| |
| static int |
| bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, |
| void *add_span_data) |
| { |
| yasm_internal_error(N_("bc_insn_calc_len() is not implemented")); |
| /*@notreached@*/ |
| return 0; |
| } |
| |
| static int |
| bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@unused@*/ yasm_output_reloc_func output_reloc) |
| { |
| yasm_internal_error(N_("bc_insn_tobytes() is not implemented")); |
| /*@notreached@*/ |
| return 1; |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_insn(yasm_arch *arch, const uintptr_t insn_data[4], |
| int num_operands, /*@null@*/ yasm_insn_operands *operands, |
| unsigned long line) |
| { |
| bytecode_insn *insn = yasm_xmalloc(sizeof(bytecode_insn)); |
| |
| insn->arch = arch; |
| insn->insn_data[0] = insn_data[0]; |
| insn->insn_data[1] = insn_data[1]; |
| insn->insn_data[2] = insn_data[2]; |
| insn->insn_data[3] = insn_data[3]; |
| insn->num_operands = num_operands; |
| if (operands) |
| insn->operands = *operands; /* structure copy */ |
| else |
| yasm_ops_initialize(&insn->operands); |
| insn->num_prefixes = 0; |
| insn->prefixes = NULL; |
| insn->num_segregs = 0; |
| insn->segregs = NULL; |
| |
| return yasm_bc_create_common(&bc_insn_callback, insn, line); |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_empty_insn(yasm_arch *arch, unsigned long line) |
| { |
| bytecode_insn *insn = yasm_xmalloc(sizeof(bytecode_insn)); |
| |
| insn->arch = arch; |
| insn->insn_data[0] = 0; |
| insn->insn_data[1] = 0; |
| insn->insn_data[2] = 0; |
| insn->insn_data[3] = 0; |
| insn->num_operands = 0; |
| yasm_ops_initialize(&insn->operands); |
| insn->num_prefixes = 0; |
| insn->prefixes = NULL; |
| insn->num_segregs = 0; |
| insn->segregs = NULL; |
| |
| return yasm_bc_create_common(&bc_insn_callback, insn, line); |
| } |
| |
| void |
| yasm_bc_insn_add_prefix(yasm_bytecode *bc, const uintptr_t prefix_data[4]) |
| { |
| bytecode_insn *insn = (bytecode_insn *)bc->contents; |
| |
| assert(bc->callback == bc_insn_callback); |
| |
| insn->prefixes = |
| yasm_xrealloc(insn->prefixes, |
| (insn->num_prefixes+1)*sizeof(uintptr_t *)); |
| insn->prefixes[insn->num_prefixes] = yasm_xmalloc(4*sizeof(uintptr_t)); |
| insn->prefixes[insn->num_prefixes][0] = prefix_data[0]; |
| insn->prefixes[insn->num_prefixes][1] = prefix_data[1]; |
| insn->prefixes[insn->num_prefixes][2] = prefix_data[2]; |
| insn->prefixes[insn->num_prefixes][3] = prefix_data[3]; |
| insn->num_prefixes++; |
| } |
| |
| void |
| yasm_bc_insn_add_seg_prefix(yasm_bytecode *bc, uintptr_t segreg) |
| { |
| bytecode_insn *insn = (bytecode_insn *)bc->contents; |
| |
| assert(bc->callback == bc_insn_callback); |
| |
| insn->segregs = |
| yasm_xrealloc(insn->segregs, (insn->num_segregs+1)*sizeof(uintptr_t)); |
| insn->segregs[insn->num_segregs] = segreg; |
| insn->num_segregs++; |
| } |