/* * Data (and LEB128) bytecode * * 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: bc-data.c 2133 2008-10-07 05:59:29Z peter $"); #include "libyasm-stdint.h" #include "coretype.h" #include "errwarn.h" #include "intnum.h" #include "expr.h" #include "value.h" #include "bytecode.h" #include "arch.h" struct yasm_dataval { /*@reldef@*/ STAILQ_ENTRY(yasm_dataval) link; enum { DV_EMPTY, DV_VALUE, DV_RAW, DV_ULEB128, DV_SLEB128, DV_RESERVE } type; union { yasm_value val; struct { /*@only@*/ unsigned char *contents; unsigned long len; } raw; } data; /* number of times data is repeated, NULL=1. */ /*@only@*/ /*@null@*/ yasm_expr *multiple; }; typedef struct bytecode_data { /* converted data (linked list) */ yasm_datavalhead datahead; int item_size; } bytecode_data; 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 int bc_data_item_size(yasm_bytecode *bc); static int bc_data_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data); 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 const yasm_bytecode_callback bc_data_callback = { bc_data_destroy, bc_data_print, bc_data_finalize, bc_data_item_size, bc_data_calc_len, yasm_bc_expand_common, bc_data_tobytes, 0 }; static void bc_data_destroy(void *contents) { bytecode_data *bc_data = (bytecode_data *)contents; yasm_dvs_delete(&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, "%*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; yasm_intnum *intn; /* Convert values from simple expr to value. */ STAILQ_FOREACH(dv, &bc_data->datahead, link) { switch (dv->type) { case DV_VALUE: if (yasm_value_finalize(&dv->data.val, prev_bc)) { yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("data expression too complex")); return; } break; case DV_ULEB128: case DV_SLEB128: intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); if (!intn) { yasm_error_set(YASM_ERROR_NOT_CONSTANT, 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 && dv->type == DV_ULEB128) yasm_warn_set(YASM_WARN_GENERAL, N_("negative value in unsigned LEB128")); break; default: break; } if (dv->multiple) { yasm_value val; if (yasm_value_finalize_expr(&val, dv->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")); dv->multiple = val.abs; } } } static int bc_data_item_size(yasm_bytecode *bc) { bytecode_data *bc_data = (bytecode_data *)bc->contents; return bc_data->item_size; } static int bc_data_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data) { bytecode_data *bc_data = (bytecode_data *)bc->contents; yasm_dataval *dv; yasm_intnum *intn; unsigned long len = 0; unsigned long multiple; /* Count up element sizes, rounding up string length. */ STAILQ_FOREACH(dv, &bc_data->datahead, link) { switch (dv->type) { case DV_EMPTY: len = 0; break; case DV_VALUE: len = dv->data.val.size/8; break; case DV_RAW: len = dv->data.raw.len; break; case DV_ULEB128: case DV_SLEB128: intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); if (!intn) yasm_internal_error(N_("non-constant in data_tobytes")); len = yasm_intnum_size_leb128(intn, dv->type == DV_SLEB128); break; case DV_RESERVE: len = dv->data.val.size/8; break; } if (!yasm_dv_get_multiple(dv, &multiple)) len *= multiple; bc->len += len; } return 0; } 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; unsigned char *bufp_orig = *bufp; yasm_intnum *intn; unsigned int val_len; unsigned long multiple, i; STAILQ_FOREACH(dv, &bc_data->datahead, link) { if (yasm_dv_get_multiple(dv, &multiple) || multiple == 0) continue; switch (dv->type) { case DV_EMPTY: break; case DV_VALUE: val_len = dv->data.val.size/8; for (i=0; idata.val, *bufp, val_len, (unsigned long)(*bufp-bufp_orig), bc, 1, d)) return 1; *bufp += val_len; } break; case DV_RAW: for (i=0; idata.raw.contents, dv->data.raw.len); *bufp += dv->data.raw.len; } break; case DV_ULEB128: case DV_SLEB128: intn = yasm_expr_get_intnum(&dv->data.val.abs, 234); if (!intn) yasm_internal_error(N_("non-constant in data_tobytes")); for (i=0; itype == DV_SLEB128); } case DV_RESERVE: val_len = dv->data.val.size/8; for (i=0; idatahead); data->item_size = size; /* Prescan input data for length, etc. Careful: this needs to be * precisely paired with the second loop. */ STAILQ_FOREACH(dv, datahead, link) { if (dv->multiple && dv->type != DV_EMPTY && len > 0) { /* Flush previous data */ dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); STAILQ_INSERT_TAIL(&data->datahead, dvo, link); len = 0; } switch (dv->type) { case DV_EMPTY: break; case DV_VALUE: case DV_ULEB128: case DV_SLEB128: intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); if (intn && dv->type == DV_VALUE && (arch || size == 1)) len += size; else if (intn && dv->type == DV_ULEB128) len += yasm_intnum_size_leb128(intn, 0); else if (intn && dv->type == DV_SLEB128) len += yasm_intnum_size_leb128(intn, 1); else { if (len > 0) { /* Create bytecode for all previous len */ dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); STAILQ_INSERT_TAIL(&data->datahead, dvo, link); len = 0; } /* Create bytecode for this value */ dvo = yasm_xmalloc(sizeof(yasm_dataval)); STAILQ_INSERT_TAIL(&data->datahead, dvo, link); dvo->multiple = dv->multiple; } break; case DV_RAW: rlen = dv->data.raw.len; /* find count, rounding up to nearest multiple of size */ rlen = (rlen + size - 1) / size; len += rlen*size; break; case DV_RESERVE: len += size; break; } if (dv->multiple && dv->type != DV_EMPTY && len > 0) { /* Flush this data */ dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); STAILQ_INSERT_TAIL(&data->datahead, dvo, link); dvo->multiple = dv->multiple; len = 0; } if (append_zero) len++; } /* Create final dataval for any trailing length */ if (len > 0) { dvo = yasm_dv_create_raw(yasm_xmalloc(len), len); STAILQ_INSERT_TAIL(&data->datahead, dvo, link); } /* Second iteration: copy data and delete input datavals. */ dv = STAILQ_FIRST(datahead); dvo = STAILQ_FIRST(&data->datahead); len = 0; while (dv && dvo) { if (dv->multiple && dv->type != DV_EMPTY && len > 0) { dvo = STAILQ_NEXT(dvo, link); len = 0; } switch (dv->type) { case DV_EMPTY: break; case DV_VALUE: case DV_ULEB128: case DV_SLEB128: intn = yasm_expr_get_intnum(&dv->data.val.abs, 0); if (intn && dv->type == DV_VALUE && (arch || size == 1)) { if (size == 1) yasm_intnum_get_sized(intn, &dvo->data.raw.contents[len], 1, 8, 0, 0, 1); else yasm_arch_intnum_tobytes(arch, intn, &dvo->data.raw.contents[len], size, size*8, 0, bc, 1); yasm_value_delete(&dv->data.val); len += size; } else if (intn && dv->type == DV_ULEB128) { len += yasm_intnum_get_leb128(intn, &dvo->data.raw.contents[len], 0); yasm_value_delete(&dv->data.val); } else if (intn && dv->type == DV_SLEB128) { len += yasm_intnum_get_leb128(intn, &dvo->data.raw.contents[len], 1); yasm_value_delete(&dv->data.val); } else { if (len > 0) dvo = STAILQ_NEXT(dvo, link); dvo->type = dv->type; dvo->data.val = dv->data.val; /* structure copy */ dvo->data.val.size = size*8; /* remember size */ dvo = STAILQ_NEXT(dvo, link); len = 0; } break; case DV_RAW: rlen = dv->data.raw.len; memcpy(&dvo->data.raw.contents[len], dv->data.raw.contents, rlen); yasm_xfree(dv->data.raw.contents); len += rlen; /* pad with 0's to nearest multiple of size */ rlen %= size; if (rlen > 0) { rlen = size-rlen; for (i=0; idata.raw.contents[len++] = 0; } break; case DV_RESERVE: memset(&dvo->data.raw.contents[len], 0, size); len += size; break; } if (dv->multiple && dv->type != DV_EMPTY && len > 0) { dvo = STAILQ_NEXT(dvo, link); len = 0; } if (append_zero) dvo->data.raw.contents[len++] = 0; dv2 = STAILQ_NEXT(dv, link); yasm_xfree(dv); dv = dv2; } return bc; } yasm_bytecode * yasm_bc_create_leb128(yasm_datavalhead *datahead, int sign, unsigned long line) { yasm_dataval *dv; /* Convert all values into LEB type, error on strings/raws */ STAILQ_FOREACH(dv, datahead, link) { switch (dv->type) { case DV_VALUE: dv->type = sign ? DV_SLEB128 : DV_ULEB128; break; case DV_RAW: yasm_error_set(YASM_ERROR_VALUE, N_("LEB128 does not allow string constants")); break; default: break; } } return yasm_bc_create_data(datahead, 0, 0, 0, line); } 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, 0); retval->multiple = NULL; return retval; } yasm_dataval * yasm_dv_create_raw(unsigned char *contents, unsigned long len) { yasm_dataval *retval = yasm_xmalloc(sizeof(yasm_dataval)); retval->type = DV_RAW; retval->data.raw.contents = contents; retval->data.raw.len = len; retval->multiple = NULL; return retval; } yasm_dataval * yasm_dv_create_reserve(void) { yasm_dataval *retval = yasm_xmalloc(sizeof(yasm_dataval)); retval->type = DV_RESERVE; retval->multiple = NULL; return retval; } void yasm_dv_set_multiple(yasm_dataval *dv, yasm_expr *e) { if (dv->multiple) dv->multiple = yasm_expr_create_tree( dv->multiple, YASM_EXPR_MUL, e, e->line); else dv->multiple = e; } int yasm_dv_get_multiple(yasm_dataval *dv, unsigned long *multiple) { /*@dependent@*/ /*@null@*/ const yasm_intnum *num; *multiple = 1; if (dv->multiple) { num = yasm_expr_get_intnum(&dv->multiple, 0); 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_uint(num); } return 0; } void yasm_dvs_delete(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_RAW: yasm_xfree(cur->data.raw.contents); break; default: break; } if (cur->multiple) yasm_expr_destroy(cur->multiple); 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; unsigned long i; STAILQ_FOREACH(cur, head, link) { fprintf(f, "%*sMultiple=", indent_level, ""); if (!cur->multiple) fprintf(f, "nil (1)"); else yasm_expr_print(cur->multiple, f); 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_RAW: fprintf(f, "%*sLength=%lu\n", indent_level, "", cur->data.raw.len); fprintf(f, "%*sBytes=[", indent_level, ""); for (i=0; idata.raw.len; i++) fprintf(f, "0x%02x, ", cur->data.raw.contents[i]); fprintf(f, "]\n"); break; case DV_ULEB128: fprintf(f, "%*sULEB128 value:\n", indent_level, ""); yasm_value_print(&cur->data.val, f, indent_level+1); break; case DV_SLEB128: fprintf(f, "%*sSLEB128 value:\n", indent_level, ""); yasm_value_print(&cur->data.val, f, indent_level+1); break; case DV_RESERVE: fprintf(f, "%*sReserved\n", indent_level, ""); break; } } }