/* * 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" /*@unused@*/ RCSID("$Id: bytecode.c 1895 2007-07-14 05:31:08Z peter $"); #include "libyasm-stdint.h" #include "coretype.h" #include "errwarn.h" #include "intnum.h" #include "expr.h" #include "value.h" #include "symrec.h" #include "bytecode.h" void yasm_bc_set_multiple(yasm_bytecode *bc, yasm_expr *e) { if (bc->multiple) bc->multiple = yasm_expr_create_tree(bc->multiple, YASM_EXPR_MUL, e, e->line); else bc->multiple = e; } void yasm_bc_finalize_common(yasm_bytecode *bc, yasm_bytecode *prev_bc) { } int yasm_bc_calc_len_common(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data) { yasm_internal_error(N_("bytecode length cannot be calculated")); /*@unreached@*/ return 0; } int yasm_bc_expand_common(yasm_bytecode *bc, int span, long old_val, long new_val, /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) { yasm_internal_error(N_("bytecode does not have any dependent spans")); /*@unreached@*/ return 0; } int yasm_bc_tobytes_common(yasm_bytecode *bc, unsigned char **bufp, void *d, yasm_output_value_func output_value, /*@null@*/ yasm_output_reloc_func output_reloc) { yasm_internal_error(N_("bytecode cannot be converted to bytes")); /*@unreached@*/ return 0; } void yasm_bc_transform(yasm_bytecode *bc, const yasm_bytecode_callback *callback, void *contents) { if (bc->callback) bc->callback->destroy(bc->contents); bc->callback = callback; bc->contents = contents; } yasm_bytecode * yasm_bc_create_common(const yasm_bytecode_callback *callback, void *contents, unsigned long line) { yasm_bytecode *bc = yasm_xmalloc(sizeof(yasm_bytecode)); bc->callback = callback; bc->section = NULL; bc->multiple = (yasm_expr *)NULL; bc->len = 0; bc->mult_int = 1; bc->line = line; bc->offset = ~0UL; /* obviously incorrect / uninitialized value */ bc->symrecs = NULL; bc->contents = contents; return bc; } yasm_section * yasm_bc_get_section(yasm_bytecode *bc) { return bc->section; } void yasm_bc__add_symrec(yasm_bytecode *bc, yasm_symrec *sym) { if (!bc->symrecs) { bc->symrecs = yasm_xmalloc(2*sizeof(yasm_symrec *)); bc->symrecs[0] = sym; bc->symrecs[1] = NULL; } else { /* Very inefficient implementation for large numbers of symbols. But * that would be very unusual, so use the simple algorithm instead. */ size_t count = 1; while (bc->symrecs[count]) count++; bc->symrecs = yasm_xrealloc(bc->symrecs, (count+2)*sizeof(yasm_symrec *)); bc->symrecs[count] = sym; bc->symrecs[count+1] = NULL; } } void yasm_bc_destroy(yasm_bytecode *bc) { if (!bc) return; if (bc->callback) bc->callback->destroy(bc->contents); yasm_expr_destroy(bc->multiple); if (bc->symrecs) yasm_xfree(bc->symrecs); yasm_xfree(bc); } void yasm_bc_print(const yasm_bytecode *bc, FILE *f, int indent_level) { if (!bc->callback) fprintf(f, "%*s_Empty_\n", indent_level, ""); else bc->callback->print(bc->contents, f, indent_level); fprintf(f, "%*sMultiple=", indent_level, ""); if (!bc->multiple) fprintf(f, "nil (1)"); else yasm_expr_print(bc->multiple, f); fprintf(f, "\n%*sLength=%lu\n", indent_level, "", bc->len); fprintf(f, "%*sLine Index=%lu\n", indent_level, "", bc->line); fprintf(f, "%*sOffset=%lx\n", indent_level, "", bc->offset); } void yasm_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) { if (bc->callback) bc->callback->finalize(bc, prev_bc); if (bc->multiple) { yasm_value val; if (yasm_value_finalize_expr(&val, bc->multiple, prev_bc, 0)) yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("multiple expression too complex")); else if (val.rel) yasm_error_set(YASM_ERROR_NOT_ABSOLUTE, N_("multiple expression not absolute")); /* Finalize creates NULL output if value=0, but bc->multiple is NULL * if value=1 (this difference is to make the common case small). * However, this means we need to set bc->multiple explicitly to 0 * here if val.abs is NULL. */ if (val.abs) bc->multiple = val.abs; else bc->multiple = yasm_expr_create_ident( yasm_expr_int(yasm_intnum_create_uint(0)), bc->line); } } /*@null@*/ yasm_intnum * yasm_calc_bc_dist(yasm_bytecode *precbc1, yasm_bytecode *precbc2) { unsigned long dist2, dist1; yasm_intnum *intn; if (precbc1->section != precbc2->section) return NULL; dist1 = yasm_bc_next_offset(precbc1); dist2 = yasm_bc_next_offset(precbc2); if (dist2 < dist1) { intn = yasm_intnum_create_uint(dist1 - dist2); yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); return intn; } dist2 -= dist1; return yasm_intnum_create_uint(dist2); } unsigned long yasm_bc_next_offset(yasm_bytecode *precbc) { return precbc->offset + precbc->len*precbc->mult_int; } int yasm_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data) { int retval = 0; bc->len = 0; if (!bc->callback) yasm_internal_error(N_("got empty bytecode in yasm_bc_calc_len")); else retval = bc->callback->calc_len(bc, add_span, add_span_data); /* Check for multiples */ bc->mult_int = 1; if (bc->multiple) { /*@dependent@*/ /*@null@*/ const yasm_intnum *num; num = yasm_expr_get_intnum(&bc->multiple, 0); if (num) { if (yasm_intnum_sign(num) < 0) { yasm_error_set(YASM_ERROR_VALUE, N_("multiple is negative")); retval = -1; } else bc->mult_int = yasm_intnum_get_int(num); } else { if (yasm_expr__contains(bc->multiple, YASM_EXPR_FLOAT)) { yasm_error_set(YASM_ERROR_VALUE, N_("expression must not contain floating point value")); retval = -1; } else { yasm_value value; yasm_value_initialize(&value, bc->multiple, 0); add_span(add_span_data, bc, 0, &value, 0, 0); bc->mult_int = 0; /* assume 0 to start */ } } } /* If we got an error somewhere along the line, clear out any calc len */ if (retval < 0) bc->len = 0; return retval; } int yasm_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val, /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres) { if (span == 0) { bc->mult_int = new_val; return 1; } if (!bc->callback) { yasm_internal_error(N_("got empty bytecode in yasm_bc_expand")); /*@unreached@*/ return -1; } else return bc->callback->expand(bc, span, old_val, new_val, neg_thres, pos_thres); } /*@null@*/ /*@only@*/ unsigned char * yasm_bc_tobytes(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, /*@out@*/ int *gap, void *d, yasm_output_value_func output_value, /*@null@*/ yasm_output_reloc_func output_reloc) /*@sets *buf@*/ { /*@only@*/ /*@null@*/ unsigned char *mybuf = NULL; unsigned char *origbuf, *destbuf; long i; int error = 0; if (yasm_bc_get_multiple(bc, &bc->mult_int, 1) || bc->mult_int == 0) { *bufsize = 0; return NULL; } /* special case for reserve bytecodes */ if (bc->callback->special == YASM_BC_SPECIAL_RESERVE) { *bufsize = bc->len*bc->mult_int; *gap = 1; return NULL; /* we didn't allocate a buffer */ } *gap = 0; if (*bufsize < bc->len*bc->mult_int) { mybuf = yasm_xmalloc(bc->len*bc->mult_int); destbuf = mybuf; } else destbuf = buf; *bufsize = bc->len*bc->mult_int; if (!bc->callback) yasm_internal_error(N_("got empty bytecode in bc_tobytes")); else for (i=0; imult_int; i++) { origbuf = destbuf; error = bc->callback->tobytes(bc, &destbuf, d, output_value, output_reloc); if (!error && ((unsigned long)(destbuf - origbuf) != bc->len)) yasm_internal_error( N_("written length does not match optimized length")); } return mybuf; } int yasm_bc_get_multiple(yasm_bytecode *bc, long *multiple, int calc_bc_dist) { /*@dependent@*/ /*@null@*/ const yasm_intnum *num; *multiple = 1; if (bc->multiple) { num = yasm_expr_get_intnum(&bc->multiple, calc_bc_dist); if (!num) { yasm_error_set(YASM_ERROR_VALUE, N_("could not determine multiple")); return 1; } if (yasm_intnum_sign(num) < 0) { yasm_error_set(YASM_ERROR_VALUE, N_("multiple is negative")); return 1; } *multiple = yasm_intnum_get_int(num); } return 0; } const yasm_expr * yasm_bc_get_multiple_expr(const yasm_bytecode *bc) { return bc->multiple; } yasm_insn * yasm_bc_get_insn(yasm_bytecode *bc) { if (bc->callback->special != YASM_BC_SPECIAL_INSN) return NULL; return (yasm_insn *)bc->contents; }