| /* |
| * Bytecode utility functions |
| * |
| * Copyright (C) 2001 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 "coretype.h" |
| #include "file.h" |
| |
| #include "errwarn.h" |
| #include "intnum.h" |
| #include "expr.h" |
| #include "value.h" |
| #include "symrec.h" |
| |
| #include "bytecode.h" |
| #include "arch.h" |
| #include "objfmt.h" |
| #include "dbgfmt.h" |
| |
| #include "bc-int.h" |
| #include "expr-int.h" |
| |
| |
| struct yasm_dataval { |
| /*@reldef@*/ STAILQ_ENTRY(yasm_dataval) link; |
| |
| enum { DV_EMPTY, DV_VALUE, DV_STRING } type; |
| |
| union { |
| yasm_value val; |
| struct { |
| /*@only@*/ char *contents; |
| size_t len; |
| } str; |
| } data; |
| }; |
| |
| /* Standard bytecode types */ |
| |
| typedef struct bytecode_data { |
| /* non-converted data (linked list) */ |
| yasm_datavalhead datahead; |
| |
| /* final (converted) size of each element (in bytes) */ |
| unsigned int size; |
| |
| /* append a zero byte after each element? */ |
| int append_zero; |
| } bytecode_data; |
| |
| typedef struct bytecode_leb128 { |
| /* source data (linked list) */ |
| yasm_datavalhead datahead; |
| |
| /* signedness (0=unsigned, 1=signed) */ |
| int sign; |
| |
| /* total length (calculated at finalize time) */ |
| unsigned long len; |
| } bytecode_leb128; |
| |
| typedef struct bytecode_reserve { |
| /*@only@*/ /*@null@*/ yasm_expr *numitems; /* number of items to reserve */ |
| unsigned char itemsize; /* size of each item (in bytes) */ |
| } bytecode_reserve; |
| |
| typedef struct bytecode_incbin { |
| /*@only@*/ char *filename; /* file to include data from */ |
| |
| /* starting offset to read from (NULL=0) */ |
| /*@only@*/ /*@null@*/ yasm_expr *start; |
| |
| /* maximum number of bytes to read (NULL=no limit) */ |
| /*@only@*/ /*@null@*/ yasm_expr *maxlen; |
| } bytecode_incbin; |
| |
| typedef struct bytecode_align { |
| /*@only@*/ yasm_expr *boundary; /* alignment boundary */ |
| |
| /* What to fill intervening locations with, NULL if using code_fill */ |
| /*@only@*/ /*@null@*/ yasm_expr *fill; |
| |
| /* Maximum number of bytes to skip, NULL if no maximum. */ |
| /*@only@*/ /*@null@*/ yasm_expr *maxskip; |
| |
| /* Code fill, NULL if using 0 fill */ |
| /*@null@*/ const unsigned char **code_fill; |
| } bytecode_align; |
| |
| typedef struct bytecode_org { |
| unsigned long start; /* target starting offset within section */ |
| } bytecode_org; |
| |
| typedef struct bytecode_insn { |
| /*@dependent@*/ yasm_arch *arch; |
| unsigned long insn_data[4]; |
| |
| int num_operands; |
| /*@null@*/ yasm_insn_operands operands; |
| |
| /* array of 4-element prefix_data arrays */ |
| int num_prefixes; |
| /*@null@*/ unsigned long **prefixes; |
| |
| /* array of segment prefixes */ |
| int num_segregs; |
| /*@null@*/ unsigned long *segregs; |
| } bytecode_insn; |
| |
| /* Standard bytecode callback function prototypes */ |
| |
| static void bc_data_destroy(void *contents); |
| static void bc_data_print(const void *contents, FILE *f, int indent_level); |
| static void bc_data_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); |
| static yasm_bc_resolve_flags bc_data_resolve |
| (yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
| static int bc_data_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@null@*/ yasm_output_reloc_func output_reloc); |
| |
| static void bc_leb128_destroy(void *contents); |
| static void bc_leb128_print(const void *contents, FILE *f, int indent_level); |
| static void bc_leb128_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); |
| static yasm_bc_resolve_flags bc_leb128_resolve |
| (yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
| static int bc_leb128_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@null@*/ yasm_output_reloc_func output_reloc); |
| |
| static void bc_reserve_destroy(void *contents); |
| static void bc_reserve_print(const void *contents, FILE *f, int indent_level); |
| static void bc_reserve_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); |
| static yasm_bc_resolve_flags bc_reserve_resolve |
| (yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
| static int bc_reserve_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@null@*/ yasm_output_reloc_func output_reloc); |
| |
| static void bc_incbin_destroy(void *contents); |
| static void bc_incbin_print(const void *contents, FILE *f, int indent_level); |
| static void bc_incbin_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); |
| static yasm_bc_resolve_flags bc_incbin_resolve |
| (yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
| static int bc_incbin_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@null@*/ yasm_output_reloc_func output_reloc); |
| |
| static void bc_align_destroy(void *contents); |
| static void bc_align_print(const void *contents, FILE *f, int indent_level); |
| static void bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); |
| static yasm_bc_resolve_flags bc_align_resolve |
| (yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
| static int bc_align_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@null@*/ yasm_output_reloc_func output_reloc); |
| |
| static void bc_org_destroy(void *contents); |
| static void bc_org_print(const void *contents, FILE *f, int indent_level); |
| static void bc_org_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); |
| static yasm_bc_resolve_flags bc_org_resolve |
| (yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
| static int bc_org_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@null@*/ yasm_output_reloc_func output_reloc); |
| |
| 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 yasm_bc_resolve_flags bc_insn_resolve |
| (yasm_bytecode *bc, int save, yasm_calc_bc_dist_func calc_bc_dist); |
| 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); |
| |
| /* Standard bytecode callback structures */ |
| |
| static const yasm_bytecode_callback bc_data_callback = { |
| bc_data_destroy, |
| bc_data_print, |
| bc_data_finalize, |
| bc_data_resolve, |
| bc_data_tobytes |
| }; |
| |
| static const yasm_bytecode_callback bc_leb128_callback = { |
| bc_leb128_destroy, |
| bc_leb128_print, |
| bc_leb128_finalize, |
| bc_leb128_resolve, |
| bc_leb128_tobytes |
| }; |
| |
| static const yasm_bytecode_callback bc_reserve_callback = { |
| bc_reserve_destroy, |
| bc_reserve_print, |
| bc_reserve_finalize, |
| bc_reserve_resolve, |
| bc_reserve_tobytes |
| }; |
| |
| static const yasm_bytecode_callback bc_incbin_callback = { |
| bc_incbin_destroy, |
| bc_incbin_print, |
| bc_incbin_finalize, |
| bc_incbin_resolve, |
| bc_incbin_tobytes |
| }; |
| |
| static const yasm_bytecode_callback bc_align_callback = { |
| bc_align_destroy, |
| bc_align_print, |
| bc_align_finalize, |
| bc_align_resolve, |
| bc_align_tobytes |
| }; |
| |
| static const yasm_bytecode_callback bc_org_callback = { |
| bc_org_destroy, |
| bc_org_print, |
| bc_org_finalize, |
| bc_org_resolve, |
| bc_org_tobytes |
| }; |
| |
| static const yasm_bytecode_callback bc_insn_callback = { |
| bc_insn_destroy, |
| bc_insn_print, |
| bc_insn_finalize, |
| bc_insn_resolve, |
| bc_insn_tobytes |
| }; |
| |
| /* Static structures for when NULL is passed to conversion functions. */ |
| /* for Convert*ToBytes() */ |
| unsigned char bytes_static[16]; |
| |
| |
| yasm_immval * |
| yasm_imm_create_expr(yasm_expr *e) |
| { |
| yasm_immval *im = yasm_xmalloc(sizeof(yasm_immval)); |
| |
| if (yasm_value_finalize_expr(&im->val, e)) |
| yasm__error(e->line, N_("immediate expression too complex")); |
| im->len = 0; |
| im->sign = 0; |
| |
| return im; |
| } |
| |
| 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_len = (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, unsigned long segreg, unsigned long line) |
| { |
| if (!ea) |
| return; |
| |
| if (segreg != 0 && ea->segreg != 0) |
| yasm__warning(YASM_WARN_GENERAL, line, |
| 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, "%*sLen=%u\n", indent_level, "", (unsigned int)ea->disp_len); |
| fprintf(f, "%*sNoSplit=%u\n", indent_level, "", (unsigned int)ea->nosplit); |
| ea->callback->print(ea, f, indent_level); |
| } |
| /*@=nullstate@*/ |
| |
| 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) |
| { |
| } |
| |
| 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->line = line; |
| |
| bc->offset = 0; |
| |
| bc->opt_flags = 0; |
| |
| bc->symrecs = NULL; |
| |
| bc->contents = contents; |
| |
| return bc; |
| } |
| |
| static void |
| bc_data_destroy(void *contents) |
| { |
| bytecode_data *bc_data = (bytecode_data *)contents; |
| yasm_dvs_destroy(&bc_data->datahead); |
| yasm_xfree(contents); |
| } |
| |
| static void |
| bc_data_print(const void *contents, FILE *f, int indent_level) |
| { |
| const bytecode_data *bc_data = (const bytecode_data *)contents; |
| fprintf(f, "%*s_Data_\n", indent_level, ""); |
| fprintf(f, "%*sFinal Element Size=%u\n", indent_level+1, "", bc_data->size); |
| fprintf(f, "%*sAppend Zero=%i\n", indent_level+1, "", bc_data->append_zero); |
| fprintf(f, "%*sElements:\n", indent_level+1, ""); |
| yasm_dvs_print(&bc_data->datahead, f, indent_level+2); |
| } |
| |
| static void |
| bc_data_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) |
| { |
| bytecode_data *bc_data = (bytecode_data *)bc->contents; |
| yasm_dataval *dv; |
| |
| /* Convert values from simple expr to value. */ |
| STAILQ_FOREACH(dv, &bc_data->datahead, link) { |
| if (dv->type == DV_VALUE) { |
| if (yasm_value_finalize(&dv->data.val)) |
| yasm__error(bc->line, N_("expression too complex")); |
| } |
| } |
| } |
| |
| static yasm_bc_resolve_flags |
| bc_data_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| bytecode_data *bc_data = (bytecode_data *)bc->contents; |
| yasm_dataval *dv; |
| size_t slen; |
| |
| /* Count up element sizes, rounding up string length. */ |
| STAILQ_FOREACH(dv, &bc_data->datahead, link) { |
| switch (dv->type) { |
| case DV_EMPTY: |
| break; |
| case DV_VALUE: |
| bc->len += bc_data->size; |
| break; |
| case DV_STRING: |
| slen = dv->data.str.len; |
| /* find count, rounding up to nearest multiple of size */ |
| slen = (slen + bc_data->size - 1) / bc_data->size; |
| bc->len += slen*bc_data->size; |
| break; |
| } |
| if (bc_data->append_zero) |
| bc->len++; |
| } |
| |
| return YASM_BC_RESOLVE_MIN_LEN; |
| } |
| |
| static int |
| bc_data_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@unused@*/ yasm_output_reloc_func output_reloc) |
| { |
| bytecode_data *bc_data = (bytecode_data *)bc->contents; |
| yasm_dataval *dv; |
| size_t slen; |
| size_t i; |
| unsigned char *bufp_orig = *bufp; |
| |
| STAILQ_FOREACH(dv, &bc_data->datahead, link) { |
| switch (dv->type) { |
| case DV_EMPTY: |
| break; |
| case DV_VALUE: |
| if (output_value(&dv->data.val, *bufp, bc_data->size, |
| (size_t)(bc_data->size*8), 0, |
| (unsigned long)(*bufp-bufp_orig), bc, 1, d)) |
| return 1; |
| *bufp += bc_data->size; |
| break; |
| case DV_STRING: |
| slen = dv->data.str.len; |
| memcpy(*bufp, dv->data.str.contents, slen); |
| *bufp += slen; |
| /* pad with 0's to nearest multiple of size */ |
| slen %= bc_data->size; |
| if (slen > 0) { |
| slen = bc_data->size-slen; |
| for (i=0; i<slen; i++) |
| YASM_WRITE_8(*bufp, 0); |
| } |
| break; |
| } |
| if (bc_data->append_zero) |
| YASM_WRITE_8(*bufp, 0); |
| } |
| |
| return 0; |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_data(yasm_datavalhead *datahead, unsigned int size, |
| int append_zero, unsigned long line) |
| { |
| bytecode_data *data = yasm_xmalloc(sizeof(bytecode_data)); |
| |
| data->datahead = *datahead; |
| data->size = size; |
| data->append_zero = append_zero; |
| |
| return yasm_bc_create_common(&bc_data_callback, data, line); |
| } |
| |
| static void |
| bc_leb128_destroy(void *contents) |
| { |
| bytecode_leb128 *bc_leb128 = (bytecode_leb128 *)contents; |
| yasm_dvs_destroy(&bc_leb128->datahead); |
| yasm_xfree(contents); |
| } |
| |
| static void |
| bc_leb128_print(const void *contents, FILE *f, int indent_level) |
| { |
| const bytecode_leb128 *bc_leb128 = (const bytecode_leb128 *)contents; |
| fprintf(f, "%*s_Data_\n", indent_level, ""); |
| fprintf(f, "%*sSign=%u\n", indent_level+1, "", |
| (unsigned int)bc_leb128->sign); |
| fprintf(f, "%*sElements:\n", indent_level+1, ""); |
| yasm_dvs_print(&bc_leb128->datahead, f, indent_level+2); |
| } |
| |
| static void |
| bc_leb128_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) |
| { |
| bytecode_leb128 *bc_leb128 = (bytecode_leb128 *)bc->contents; |
| yasm_dataval *dv; |
| /*@dependent@*/ /*@null@*/ yasm_intnum *intn; |
| |
| /* Only constant expressions are allowed. |
| * Because of this, go ahead and calculate length. |
| */ |
| bc_leb128->len = 0; |
| STAILQ_FOREACH(dv, &bc_leb128->datahead, link) { |
| switch (dv->type) { |
| case DV_EMPTY: |
| break; |
| case DV_VALUE: |
| intn = yasm_expr_get_intnum(&dv->data.val.abs, NULL); |
| if (!intn) { |
| yasm__error(bc->line, |
| N_("LEB128 requires constant values")); |
| return; |
| } |
| /* Warn for negative values in unsigned environment. |
| * This could be an error instead: the likelihood this is |
| * desired is very low! |
| */ |
| if (yasm_intnum_sign(intn) == -1 && !bc_leb128->sign) |
| yasm__warning(YASM_WARN_GENERAL, bc->line, |
| N_("negative value in unsigned LEB128")); |
| bc_leb128->len += |
| yasm_intnum_size_leb128(intn, bc_leb128->sign); |
| break; |
| case DV_STRING: |
| yasm__error(bc->line, |
| N_("LEB128 does not allow string constants")); |
| return; |
| } |
| } |
| } |
| |
| static yasm_bc_resolve_flags |
| bc_leb128_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| bytecode_leb128 *bc_leb128 = (bytecode_leb128 *)bc->contents; |
| bc->len += bc_leb128->len; |
| return YASM_BC_RESOLVE_MIN_LEN; |
| } |
| |
| static int |
| bc_leb128_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@unused@*/ yasm_output_reloc_func output_reloc) |
| { |
| bytecode_leb128 *bc_leb128 = (bytecode_leb128 *)bc->contents; |
| yasm_dataval *dv; |
| /*@dependent@*/ /*@null@*/ yasm_intnum *intn; |
| |
| STAILQ_FOREACH(dv, &bc_leb128->datahead, link) { |
| switch (dv->type) { |
| case DV_EMPTY: |
| break; |
| case DV_VALUE: |
| intn = yasm_expr_get_intnum(&dv->data.val.abs, NULL); |
| if (!intn) |
| yasm_internal_error(N_("non-constant in leb128_tobytes")); |
| *bufp += yasm_intnum_get_leb128(intn, *bufp, bc_leb128->sign); |
| break; |
| case DV_STRING: |
| yasm_internal_error(N_("string in leb128_tobytes")); |
| } |
| } |
| |
| return 0; |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_leb128(yasm_datavalhead *datahead, int sign, unsigned long line) |
| { |
| bytecode_leb128 *leb128 = yasm_xmalloc(sizeof(bytecode_leb128)); |
| |
| leb128->datahead = *datahead; |
| leb128->sign = sign; |
| |
| return yasm_bc_create_common(&bc_leb128_callback, leb128, line); |
| } |
| |
| static void |
| bc_reserve_destroy(void *contents) |
| { |
| bytecode_reserve *reserve = (bytecode_reserve *)contents; |
| yasm_expr_destroy(reserve->numitems); |
| yasm_xfree(contents); |
| } |
| |
| static void |
| bc_reserve_print(const void *contents, FILE *f, int indent_level) |
| { |
| const bytecode_reserve *reserve = (const bytecode_reserve *)contents; |
| fprintf(f, "%*s_Reserve_\n", indent_level, ""); |
| fprintf(f, "%*sNum Items=", indent_level, ""); |
| yasm_expr_print(reserve->numitems, f); |
| fprintf(f, "\n%*sItem Size=%u\n", indent_level, "", |
| (unsigned int)reserve->itemsize); |
| } |
| |
| static void |
| bc_reserve_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) |
| { |
| bytecode_reserve *reserve = (bytecode_reserve *)bc->contents; |
| yasm_value val; |
| |
| if (yasm_value_finalize_expr(&val, reserve->numitems)) |
| yasm__error(bc->line, N_("expression too complex")); |
| else if (val.rel) |
| yasm__error(bc->line, N_("reserve expression not absolute")); |
| else if (val.abs && yasm_expr__contains(val.abs, YASM_EXPR_FLOAT)) |
| yasm__error(bc->line, |
| N_("expression must not contain floating point value")); |
| reserve->numitems = val.abs; |
| } |
| |
| static yasm_bc_resolve_flags |
| bc_reserve_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| bytecode_reserve *reserve = (bytecode_reserve *)bc->contents; |
| yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN; |
| /*@null@*/ yasm_expr *temp; |
| yasm_expr **tempp; |
| /*@dependent@*/ /*@null@*/ const yasm_intnum *num; |
| |
| if (!reserve->numitems) |
| return YASM_BC_RESOLVE_MIN_LEN; |
| |
| if (save) { |
| temp = NULL; |
| tempp = &reserve->numitems; |
| } else { |
| temp = yasm_expr_copy(reserve->numitems); |
| assert(temp != NULL); |
| tempp = &temp; |
| } |
| num = yasm_expr_get_intnum(tempp, calc_bc_dist); |
| if (!num) { |
| /* For reserve, just say non-constant quantity instead of allowing |
| * the circular reference error to filter through. |
| */ |
| yasm__error(bc->line, |
| N_("attempt to reserve non-constant quantity of space")); |
| retval = YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN; |
| } else |
| bc->len += yasm_intnum_get_uint(num)*reserve->itemsize; |
| yasm_expr_destroy(temp); |
| return retval; |
| } |
| |
| static int |
| bc_reserve_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_reserve_tobytes called")); |
| /*@notreached@*/ |
| return 1; |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_reserve(yasm_expr *numitems, unsigned int itemsize, |
| unsigned long line) |
| { |
| bytecode_reserve *reserve = yasm_xmalloc(sizeof(bytecode_reserve)); |
| |
| /*@-mustfree@*/ |
| reserve->numitems = numitems; |
| /*@=mustfree@*/ |
| reserve->itemsize = (unsigned char)itemsize; |
| |
| return yasm_bc_create_common(&bc_reserve_callback, reserve, line); |
| } |
| |
| static void |
| bc_incbin_destroy(void *contents) |
| { |
| bytecode_incbin *incbin = (bytecode_incbin *)contents; |
| yasm_xfree(incbin->filename); |
| yasm_expr_destroy(incbin->start); |
| yasm_expr_destroy(incbin->maxlen); |
| yasm_xfree(contents); |
| } |
| |
| static void |
| bc_incbin_print(const void *contents, FILE *f, int indent_level) |
| { |
| const bytecode_incbin *incbin = (const bytecode_incbin *)contents; |
| fprintf(f, "%*s_IncBin_\n", indent_level, ""); |
| fprintf(f, "%*sFilename=`%s'\n", indent_level, "", |
| incbin->filename); |
| fprintf(f, "%*sStart=", indent_level, ""); |
| if (!incbin->start) |
| fprintf(f, "nil (0)"); |
| else |
| yasm_expr_print(incbin->start, f); |
| fprintf(f, "%*sMax Len=", indent_level, ""); |
| if (!incbin->maxlen) |
| fprintf(f, "nil (unlimited)"); |
| else |
| yasm_expr_print(incbin->maxlen, f); |
| fprintf(f, "\n"); |
| } |
| |
| static void |
| bc_incbin_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) |
| { |
| bytecode_incbin *incbin = (bytecode_incbin *)bc->contents; |
| yasm_value val; |
| |
| if (yasm_value_finalize_expr(&val, incbin->start)) |
| yasm__error(bc->line, N_("start expression too complex")); |
| else if (val.rel) |
| yasm__error(bc->line, N_("start expression not absolute")); |
| incbin->start = val.abs; |
| |
| if (yasm_value_finalize_expr(&val, incbin->maxlen)) |
| yasm__error(bc->line, N_("maximum length expression too complex")); |
| else if (val.rel) |
| yasm__error(bc->line, N_("maximum length expression not absolute")); |
| incbin->maxlen = val.abs; |
| } |
| |
| static yasm_bc_resolve_flags |
| bc_incbin_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| bytecode_incbin *incbin = (bytecode_incbin *)bc->contents; |
| FILE *f; |
| /*@null@*/ yasm_expr *temp; |
| yasm_expr **tempp; |
| /*@dependent@*/ /*@null@*/ const yasm_intnum *num; |
| unsigned long start = 0, maxlen = 0xFFFFFFFFUL, flen; |
| |
| /* Try to convert start to integer value */ |
| if (incbin->start) { |
| if (save) { |
| temp = NULL; |
| tempp = &incbin->start; |
| } else { |
| temp = yasm_expr_copy(incbin->start); |
| assert(temp != NULL); |
| tempp = &temp; |
| } |
| num = yasm_expr_get_intnum(tempp, calc_bc_dist); |
| if (num) |
| start = yasm_intnum_get_uint(num); |
| yasm_expr_destroy(temp); |
| if (!num) |
| return YASM_BC_RESOLVE_UNKNOWN_LEN; |
| } |
| |
| /* Try to convert maxlen to integer value */ |
| if (incbin->maxlen) { |
| if (save) { |
| temp = NULL; |
| tempp = &incbin->maxlen; |
| } else { |
| temp = yasm_expr_copy(incbin->maxlen); |
| assert(temp != NULL); |
| tempp = &temp; |
| } |
| num = yasm_expr_get_intnum(tempp, calc_bc_dist); |
| if (num) |
| maxlen = yasm_intnum_get_uint(num); |
| yasm_expr_destroy(temp); |
| if (!num) |
| return YASM_BC_RESOLVE_UNKNOWN_LEN; |
| } |
| |
| /* FIXME: Search include path for filename. Save full path back into |
| * filename if save is true. |
| */ |
| |
| /* Open file and determine its length */ |
| f = fopen(incbin->filename, "rb"); |
| if (!f) { |
| yasm__error(bc->line, N_("`incbin': unable to open file `%s'"), |
| incbin->filename); |
| return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN; |
| } |
| if (fseek(f, 0L, SEEK_END) < 0) { |
| yasm__error(bc->line, N_("`incbin': unable to seek on file `%s'"), |
| incbin->filename); |
| return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN; |
| } |
| flen = (unsigned long)ftell(f); |
| fclose(f); |
| |
| /* Compute length of incbin from start, maxlen, and len */ |
| if (start > flen) { |
| yasm__warning(YASM_WARN_GENERAL, bc->line, |
| N_("`incbin': start past end of file `%s'"), |
| incbin->filename); |
| start = flen; |
| } |
| flen -= start; |
| if (incbin->maxlen) |
| if (maxlen < flen) |
| flen = maxlen; |
| bc->len += flen; |
| return YASM_BC_RESOLVE_MIN_LEN; |
| } |
| |
| static int |
| bc_incbin_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@unused@*/ yasm_output_reloc_func output_reloc) |
| { |
| bytecode_incbin *incbin = (bytecode_incbin *)bc->contents; |
| FILE *f; |
| /*@dependent@*/ /*@null@*/ const yasm_intnum *num; |
| unsigned long start = 0; |
| |
| /* Convert start to integer value */ |
| if (incbin->start) { |
| num = yasm_expr_get_intnum(&incbin->start, NULL); |
| if (!num) |
| yasm_internal_error( |
| N_("could not determine start in bc_tobytes_incbin")); |
| start = yasm_intnum_get_uint(num); |
| } |
| |
| /* Open file */ |
| f = fopen(incbin->filename, "rb"); |
| if (!f) { |
| yasm__error(bc->line, N_("`incbin': unable to open file `%s'"), |
| incbin->filename); |
| return 1; |
| } |
| |
| /* Seek to start of data */ |
| if (fseek(f, (long)start, SEEK_SET) < 0) { |
| yasm__error(bc->line, N_("`incbin': unable to seek on file `%s'"), |
| incbin->filename); |
| fclose(f); |
| return 1; |
| } |
| |
| /* Read len bytes */ |
| if (fread(*bufp, 1, (size_t)bc->len, f) < (size_t)bc->len) { |
| yasm__error(bc->line, |
| N_("`incbin': unable to read %lu bytes from file `%s'"), |
| bc->len, incbin->filename); |
| fclose(f); |
| return 1; |
| } |
| |
| *bufp += bc->len; |
| fclose(f); |
| return 0; |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_incbin(char *filename, yasm_expr *start, yasm_expr *maxlen, |
| unsigned long line) |
| { |
| bytecode_incbin *incbin = yasm_xmalloc(sizeof(bytecode_incbin)); |
| |
| /*@-mustfree@*/ |
| incbin->filename = filename; |
| incbin->start = start; |
| incbin->maxlen = maxlen; |
| /*@=mustfree@*/ |
| |
| return yasm_bc_create_common(&bc_incbin_callback, incbin, line); |
| } |
| |
| static void |
| bc_align_destroy(void *contents) |
| { |
| bytecode_align *align = (bytecode_align *)contents; |
| if (align->boundary) |
| yasm_expr_destroy(align->boundary); |
| if (align->fill) |
| yasm_expr_destroy(align->fill); |
| if (align->maxskip) |
| yasm_expr_destroy(align->maxskip); |
| yasm_xfree(contents); |
| } |
| |
| static void |
| bc_align_print(const void *contents, FILE *f, int indent_level) |
| { |
| const bytecode_align *align = (const bytecode_align *)contents; |
| fprintf(f, "%*s_Align_\n", indent_level, ""); |
| fprintf(f, "%*sBoundary=", indent_level, ""); |
| yasm_expr_print(align->boundary, f); |
| fprintf(f, "\n%*sFill=", indent_level, ""); |
| yasm_expr_print(align->fill, f); |
| fprintf(f, "\n%*sMax Skip=", indent_level, ""); |
| yasm_expr_print(align->maxskip, f); |
| fprintf(f, "\n"); |
| } |
| |
| static void |
| bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) |
| { |
| bytecode_align *align = (bytecode_align *)bc->contents; |
| if (!yasm_expr_get_intnum(&align->boundary, NULL)) |
| yasm__error(bc->line, N_("align boundary must be a constant")); |
| if (align->fill && !yasm_expr_get_intnum(&align->fill, NULL)) |
| yasm__error(bc->line, N_("align fill must be a constant")); |
| if (align->maxskip && !yasm_expr_get_intnum(&align->maxskip, NULL)) |
| yasm__error(bc->line, N_("align maximum skip must be a constant")); |
| } |
| |
| static yasm_bc_resolve_flags |
| bc_align_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| bytecode_align *align = (bytecode_align *)bc->contents; |
| unsigned long end; |
| unsigned long boundary = |
| yasm_intnum_get_uint(yasm_expr_get_intnum(&align->boundary, NULL)); |
| |
| if (boundary == 0) { |
| bc->len = 0; |
| return YASM_BC_RESOLVE_MIN_LEN; |
| } |
| |
| end = bc->offset; |
| if (bc->offset & (boundary-1)) |
| end = (bc->offset & ~(boundary-1)) + boundary; |
| |
| bc->len = end - bc->offset; |
| |
| if (align->maxskip) { |
| unsigned long maxskip = |
| yasm_intnum_get_uint(yasm_expr_get_intnum(&align->maxskip, NULL)); |
| if ((end - bc->offset) > maxskip) |
| bc->len = 0; |
| } |
| return YASM_BC_RESOLVE_MIN_LEN; |
| } |
| |
| static int |
| bc_align_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@unused@*/ yasm_output_reloc_func output_reloc) |
| { |
| bytecode_align *align = (bytecode_align *)bc->contents; |
| unsigned long len; |
| unsigned long boundary = |
| yasm_intnum_get_uint(yasm_expr_get_intnum(&align->boundary, NULL)); |
| |
| if (boundary == 0) |
| return 0; |
| else { |
| unsigned long end = bc->offset; |
| if (bc->offset & (boundary-1)) |
| end = (bc->offset & ~(boundary-1)) + boundary; |
| len = end - bc->offset; |
| if (len == 0) |
| return 0; |
| if (align->maxskip) { |
| unsigned long maxskip = |
| yasm_intnum_get_uint(yasm_expr_get_intnum(&align->maxskip, |
| NULL)); |
| if (len > maxskip) |
| return 0; |
| } |
| } |
| |
| if (align->fill) { |
| unsigned long v; |
| v = yasm_intnum_get_uint(yasm_expr_get_intnum(&align->fill, NULL)); |
| memset(*bufp, (int)v, len); |
| *bufp += len; |
| } else if (align->code_fill) { |
| unsigned long maxlen = 15; |
| while (!align->code_fill[maxlen] && maxlen>0) |
| maxlen--; |
| if (maxlen == 0) { |
| yasm__error(bc->line, N_("could not find any code alignment size")); |
| return 1; |
| } |
| |
| /* Fill with maximum code fill as much as possible */ |
| while (len > maxlen) { |
| memcpy(*bufp, align->code_fill[maxlen], maxlen); |
| *bufp += maxlen; |
| len -= maxlen; |
| } |
| |
| if (!align->code_fill[len]) { |
| yasm__error(bc->line, N_("invalid alignment size %d"), len); |
| return 1; |
| } |
| /* Handle rest of code fill */ |
| memcpy(*bufp, align->code_fill[len], len); |
| *bufp += len; |
| } else { |
| /* Just fill with 0 */ |
| memset(*bufp, 0, len); |
| *bufp += len; |
| } |
| return 0; |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_align(yasm_expr *boundary, yasm_expr *fill, |
| yasm_expr *maxskip, const unsigned char **code_fill, |
| unsigned long line) |
| { |
| bytecode_align *align = yasm_xmalloc(sizeof(bytecode_align)); |
| |
| align->boundary = boundary; |
| align->fill = fill; |
| align->maxskip = maxskip; |
| align->code_fill = code_fill; |
| |
| return yasm_bc_create_common(&bc_align_callback, align, line); |
| } |
| |
| static void |
| bc_org_destroy(void *contents) |
| { |
| yasm_xfree(contents); |
| } |
| |
| static void |
| bc_org_print(const void *contents, FILE *f, int indent_level) |
| { |
| const bytecode_org *org = (const bytecode_org *)contents; |
| fprintf(f, "%*s_Org_\n", indent_level, ""); |
| fprintf(f, "%*sStart=%lu\n", indent_level, "", org->start); |
| } |
| |
| static void |
| bc_org_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) |
| { |
| } |
| |
| static yasm_bc_resolve_flags |
| bc_org_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| bytecode_org *org = (bytecode_org *)bc->contents; |
| |
| /* Check for overrun */ |
| if (bc->offset > org->start) { |
| yasm__error(bc->line, N_("ORG overlap with already existing data")); |
| return YASM_BC_RESOLVE_ERROR | YASM_BC_RESOLVE_UNKNOWN_LEN; |
| } |
| |
| /* Generate space to start offset */ |
| bc->len = org->start - bc->offset; |
| return YASM_BC_RESOLVE_MIN_LEN; |
| } |
| |
| static int |
| bc_org_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, |
| yasm_output_value_func output_value, |
| /*@unused@*/ yasm_output_reloc_func output_reloc) |
| { |
| bytecode_org *org = (bytecode_org *)bc->contents; |
| unsigned long len, i; |
| |
| /* Sanity check for overrun */ |
| if (bc->offset > org->start) { |
| yasm__error(bc->line, N_("ORG overlap with already existing data")); |
| return 1; |
| } |
| len = org->start - bc->offset; |
| for (i=0; i<len; i++) |
| YASM_WRITE_8(*bufp, 0); |
| return 0; |
| } |
| |
| yasm_bytecode * |
| yasm_bc_create_org(unsigned long start, unsigned long line) |
| { |
| bytecode_org *org = yasm_xmalloc(sizeof(bytecode_org)); |
| |
| org->start = start; |
| |
| return yasm_bc_create_common(&bc_org_callback, org, line); |
| } |
| |
| 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; |
| int error = 0; |
| yasm_insn_operand *op; |
| |
| /* 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, |
| NULL, NULL, NULL, NULL, &error); |
| if (error) { |
| /* Follow up error with a pointer to where it was used */ |
| yasm__error(bc->line, N_("(used in memory expression)")); |
| return; |
| } |
| break; |
| case YASM_INSN__OPERAND_IMM: |
| op->data.val = |
| yasm_expr__level_tree(op->data.val, 1, 1, 1, NULL, NULL, |
| NULL, NULL, &error); |
| if (error) { |
| /* Follow up error with a pointer to where it was used */ |
| yasm__error(bc->line, |
| N_("(used in immediate expression)")); |
| 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 yasm_bc_resolve_flags |
| bc_insn_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| yasm_internal_error(N_("bc_insn_resolve() is not implemented")); |
| /*@notreached@*/ |
| return YASM_BC_RESOLVE_ERROR; |
| } |
| |
| 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 unsigned long 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 unsigned long 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(unsigned long *)); |
| insn->prefixes[insn->num_prefixes] = |
| yasm_xmalloc(4*sizeof(unsigned long)); |
| 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, unsigned long 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(unsigned long)); |
| insn->segregs[insn->num_segregs] = segreg; |
| insn->num_segregs++; |
| } |
| |
| 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)) |
| yasm__error(bc->line, N_("multiple expression too complex")); |
| else if (val.rel) |
| yasm__error(bc->line, N_("multiple expression not absolute")); |
| bc->multiple = val.abs; |
| } |
| } |
| |
| /*@null@*/ yasm_intnum * |
| yasm_common_calc_bc_dist(yasm_bytecode *precbc1, yasm_bytecode *precbc2) |
| { |
| unsigned long dist; |
| yasm_intnum *intn; |
| |
| if (precbc1->section != precbc2->section) |
| return NULL; |
| |
| dist = precbc2->offset + precbc2->len; |
| if (dist < precbc1->offset + precbc1->len) { |
| intn = yasm_intnum_create_uint(precbc1->offset + precbc1->len - dist); |
| yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL, precbc1->line); |
| return intn; |
| } |
| dist -= precbc1->offset + precbc1->len; |
| return yasm_intnum_create_uint(dist); |
| } |
| |
| yasm_bc_resolve_flags |
| yasm_bc_resolve(yasm_bytecode *bc, int save, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| yasm_bc_resolve_flags retval = YASM_BC_RESOLVE_MIN_LEN; |
| /*@null@*/ yasm_expr *temp; |
| yasm_expr **tempp; |
| /*@dependent@*/ /*@null@*/ const yasm_intnum *num; |
| |
| bc->len = 0; /* start at 0 */ |
| |
| if (!bc->callback) |
| yasm_internal_error(N_("got empty bytecode in bc_resolve")); |
| else |
| retval = bc->callback->resolve(bc, save, calc_bc_dist); |
| |
| /* Multiply len by number of multiples */ |
| if (bc->multiple) { |
| if (save) { |
| temp = NULL; |
| tempp = &bc->multiple; |
| } else { |
| temp = yasm_expr_copy(bc->multiple); |
| assert(temp != NULL); |
| tempp = &temp; |
| } |
| num = yasm_expr_get_intnum(tempp, calc_bc_dist); |
| if (!num) { |
| retval = YASM_BC_RESOLVE_UNKNOWN_LEN; |
| if (temp && yasm_expr__contains(temp, YASM_EXPR_FLOAT)) { |
| yasm__error(bc->line, |
| N_("expression must not contain floating point value")); |
| retval |= YASM_BC_RESOLVE_ERROR; |
| } |
| } else { |
| if (yasm_intnum_sign(num) >= 0) |
| bc->len *= yasm_intnum_get_uint(num); |
| else |
| retval |= YASM_BC_RESOLVE_ERROR; |
| } |
| yasm_expr_destroy(temp); |
| } |
| |
| /* If we got an error somewhere along the line, clear out any calc len */ |
| if (retval & YASM_BC_RESOLVE_UNKNOWN_LEN) |
| bc->len = 0; |
| |
| return retval; |
| } |
| |
| /*@null@*/ /*@only@*/ unsigned char * |
| yasm_bc_tobytes(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize, |
| /*@out@*/ unsigned long *multiple, /*@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; |
| /*@dependent@*/ /*@null@*/ const yasm_intnum *num; |
| unsigned long datasize; |
| int error = 0; |
| |
| if (bc->multiple) { |
| num = yasm_expr_get_intnum(&bc->multiple, NULL); |
| if (!num) |
| yasm_internal_error( |
| N_("could not determine multiple in bc_tobytes")); |
| if (yasm_intnum_sign(num) < 0) { |
| yasm__error(bc->line, N_("multiple is negative")); |
| *bufsize = 0; |
| return NULL; |
| } |
| *multiple = yasm_intnum_get_uint(num); |
| if (*multiple == 0) { |
| *bufsize = 0; |
| return NULL; |
| } |
| } else |
| *multiple = 1; |
| |
| datasize = bc->len / (*multiple); |
| |
| /* special case for reserve bytecodes */ |
| if (bc->callback == &bc_reserve_callback) { |
| *bufsize = datasize; |
| *gap = 1; |
| return NULL; /* we didn't allocate a buffer */ |
| } |
| |
| *gap = 0; |
| |
| if (*bufsize < datasize) { |
| mybuf = yasm_xmalloc(bc->len); |
| origbuf = mybuf; |
| destbuf = mybuf; |
| } else { |
| origbuf = buf; |
| destbuf = buf; |
| } |
| *bufsize = datasize; |
| |
| if (!bc->callback) |
| yasm_internal_error(N_("got empty bytecode in bc_tobytes")); |
| else |
| error = bc->callback->tobytes(bc, &destbuf, d, output_value, |
| output_reloc); |
| |
| if (!error && ((unsigned long)(destbuf - origbuf) != datasize)) |
| yasm_internal_error( |
| N_("written length does not match optimized length")); |
| return mybuf; |
| } |
| |
| yasm_dataval * |
| yasm_dv_create_expr(yasm_expr *e) |
| { |
| yasm_dataval *retval = yasm_xmalloc(sizeof(yasm_dataval)); |
| |
| retval->type = DV_VALUE; |
| yasm_value_initialize(&retval->data.val, e); |
| |
| return retval; |
| } |
| |
| yasm_dataval * |
| yasm_dv_create_string(char *contents, size_t len) |
| { |
| yasm_dataval *retval = yasm_xmalloc(sizeof(yasm_dataval)); |
| |
| retval->type = DV_STRING; |
| retval->data.str.contents = contents; |
| retval->data.str.len = len; |
| |
| return retval; |
| } |
| |
| void |
| yasm_dvs_destroy(yasm_datavalhead *headp) |
| { |
| yasm_dataval *cur, *next; |
| |
| cur = STAILQ_FIRST(headp); |
| while (cur) { |
| next = STAILQ_NEXT(cur, link); |
| switch (cur->type) { |
| case DV_VALUE: |
| yasm_value_delete(&cur->data.val); |
| break; |
| case DV_STRING: |
| yasm_xfree(cur->data.str.contents); |
| break; |
| default: |
| break; |
| } |
| yasm_xfree(cur); |
| cur = next; |
| } |
| STAILQ_INIT(headp); |
| } |
| |
| yasm_dataval * |
| yasm_dvs_append(yasm_datavalhead *headp, yasm_dataval *dv) |
| { |
| if (dv) { |
| STAILQ_INSERT_TAIL(headp, dv, link); |
| return dv; |
| } |
| return (yasm_dataval *)NULL; |
| } |
| |
| void |
| yasm_dvs_print(const yasm_datavalhead *head, FILE *f, int indent_level) |
| { |
| yasm_dataval *cur; |
| |
| STAILQ_FOREACH(cur, head, link) { |
| switch (cur->type) { |
| case DV_EMPTY: |
| fprintf(f, "%*sEmpty\n", indent_level, ""); |
| break; |
| case DV_VALUE: |
| fprintf(f, "%*sValue:\n", indent_level, ""); |
| yasm_value_print(&cur->data.val, f, indent_level+1); |
| break; |
| case DV_STRING: |
| fprintf(f, "%*sLength=%lu\n", indent_level, "", |
| (unsigned long)cur->data.str.len); |
| fprintf(f, "%*sString=\"%s\"\n", indent_level, "", |
| cur->data.str.contents); |
| break; |
| } |
| } |
| } |
| |
| /* Non-macro yasm_dvs_initialize() for non-YASM_LIB_INTERNAL users. */ |
| #undef yasm_dvs_initialize |
| void |
| yasm_dvs_initialize(yasm_datavalhead *headp) |
| { |
| STAILQ_INIT(headp); |
| } |