| /* |
| * Value handling |
| * |
| * Copyright (C) 2006 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 "bitvect.h" |
| |
| #include "errwarn.h" |
| #include "intnum.h" |
| #include "floatnum.h" |
| #include "expr.h" |
| #include "value.h" |
| #include "symrec.h" |
| |
| #include "bytecode.h" |
| #include "section.h" |
| |
| #include "arch.h" |
| |
| #include "expr-int.h" |
| #include "bc-int.h" |
| |
| static int |
| value_finalize_scan(yasm_value *value, yasm_expr *e, int ssym_not_ok) |
| { |
| int i; |
| /*@dependent@*/ yasm_section *sect; |
| /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; |
| |
| unsigned long shamt; /* for SHR */ |
| |
| /* Yes, this has a maximum upper bound on 32 terms, based on an |
| * "insane number of terms" (and ease of implementation) WAG. |
| * The right way to do this would be a stack-based alloca, but that's |
| * not ISO C. We really don't want to malloc here as this function is |
| * hit a lot! |
| * |
| * This is a bitmask to keep things small, as this is a recursive |
| * routine and we don't want to eat up stack space. |
| */ |
| unsigned long used; /* for ADD */ |
| |
| /* Thanks to this running after a simplify, we don't need to iterate |
| * down through IDENTs or handle SUB. |
| * |
| * We scan for a single symrec, gathering info along the way. After |
| * we've found the symrec, we keep scanning but error if we find |
| * another one. We pull out the single symrec and any legal operations |
| * performed on it. |
| * |
| * Also, if we find a float anywhere, we don't allow mixing of a single |
| * symrec with it. |
| */ |
| switch (e->op) { |
| case YASM_EXPR_ADD: |
| /* Okay for single symrec anywhere in expr. |
| * Check for single symrec anywhere. |
| * Handle symrec-symrec by checking for (-1*symrec) |
| * and symrec term pairs (where both symrecs are in the same |
| * segment). |
| */ |
| if (e->numterms > 32) |
| yasm__fatal(N_("expression on line %d has too many add terms;" |
| " internal limit of 32"), e->line); |
| |
| used = 0; |
| |
| for (i=0; i<e->numterms; i++) { |
| int j; |
| yasm_expr *sube; |
| yasm_intnum *intn; |
| yasm_symrec *sym; |
| /*@dependent@*/ yasm_section *sect2; |
| /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc2; |
| |
| /* First look for an (-1*symrec) term */ |
| if (e->terms[i].type != YASM_EXPR_EXPR) |
| continue; |
| sube = e->terms[i].data.expn; |
| |
| if (sube->op != YASM_EXPR_MUL || sube->numterms != 2) { |
| /* recurse instead */ |
| if (value_finalize_scan(value, sube, ssym_not_ok)) |
| return 1; |
| continue; |
| } |
| |
| if (sube->terms[0].type == YASM_EXPR_INT && |
| (sube->terms[1].type == YASM_EXPR_SYM || |
| sube->terms[1].type == YASM_EXPR_SYMEXP)) { |
| intn = sube->terms[0].data.intn; |
| sym = sube->terms[1].data.sym; |
| } else if ((sube->terms[0].type == YASM_EXPR_SYM || |
| sube->terms[0].type == YASM_EXPR_SYMEXP) && |
| sube->terms[1].type == YASM_EXPR_INT) { |
| sym = sube->terms[0].data.sym; |
| intn = sube->terms[1].data.intn; |
| } else { |
| if (value_finalize_scan(value, sube, ssym_not_ok)) |
| return 1; |
| continue; |
| } |
| |
| if (!yasm_intnum_is_neg1(intn)) { |
| if (value_finalize_scan(value, sube, ssym_not_ok)) |
| return 1; |
| continue; |
| } |
| |
| if (!yasm_symrec_get_label(sym, &precbc)) { |
| if (value_finalize_scan(value, sube, ssym_not_ok)) |
| return 1; |
| continue; |
| } |
| sect2 = yasm_bc_get_section(precbc); |
| |
| /* Now look for a unused symrec term in the same segment */ |
| for (j=0; j<e->numterms; j++) { |
| if ((e->terms[j].type == YASM_EXPR_SYM |
| || e->terms[j].type == YASM_EXPR_SYMEXP) |
| && yasm_symrec_get_label(e->terms[j].data.sym, |
| &precbc2) |
| && (sect = yasm_bc_get_section(precbc2)) |
| && sect == sect2 |
| && (used & (1<<j)) == 0) { |
| /* Mark as used */ |
| used |= 1<<j; |
| break; /* stop looking */ |
| } |
| } |
| |
| /* We didn't match in the same segment. If the |
| * -1*symrec is actually -1*curpos, we can match |
| * unused symrec terms in other segments and generate |
| * a curpos-relative reloc. |
| * |
| * Don't do this if we've already become curpos-relative. |
| * The unmatched symrec will be caught below. |
| */ |
| if (j == e->numterms && yasm_symrec_is_curpos(sym) |
| && !value->curpos_rel) { |
| for (j=0; j<e->numterms; j++) { |
| if ((e->terms[j].type == YASM_EXPR_SYM |
| || e->terms[j].type == YASM_EXPR_SYMEXP) |
| && yasm_symrec_get_label(e->terms[j].data.sym, |
| &precbc2) |
| && (used & (1<<j)) == 0) { |
| /* Mark as used */ |
| used |= 1<<j; |
| /* Mark value as curpos-relative */ |
| if (value->rel || ssym_not_ok) |
| return 1; |
| value->rel = e->terms[j].data.sym; |
| value->curpos_rel = 1; |
| /* Replace both symrec portions with 0 */ |
| yasm_expr_destroy(sube); |
| e->terms[i].type = YASM_EXPR_INT; |
| e->terms[i].data.intn = yasm_intnum_create_uint(0); |
| e->terms[j].type = YASM_EXPR_INT; |
| e->terms[j].data.intn = yasm_intnum_create_uint(0); |
| break; /* stop looking */ |
| } |
| } |
| } |
| |
| if (j == e->numterms) |
| return 1; /* We didn't find a match! */ |
| } |
| |
| /* Look for unmatched symrecs. If we've already found one or |
| * we don't WANT to find one, error out. |
| */ |
| for (i=0; i<e->numterms; i++) { |
| if ((e->terms[i].type == YASM_EXPR_SYM |
| || e->terms[i].type == YASM_EXPR_SYMEXP) |
| && (used & (1<<i)) == 0) { |
| if (value->rel || ssym_not_ok) |
| return 1; |
| value->rel = e->terms[i].data.sym; |
| /* and replace with 0 */ |
| e->terms[i].type = YASM_EXPR_INT; |
| e->terms[i].data.intn = yasm_intnum_create_uint(0); |
| } |
| } |
| break; |
| case YASM_EXPR_SHR: |
| /* Okay for single symrec in LHS and constant on RHS. |
| * Single symrecs are not okay on RHS. |
| * If RHS is non-constant, don't allow single symrec on LHS. |
| * XXX: should rshift be an expr instead?? |
| */ |
| |
| /* Check for not allowed cases on RHS */ |
| switch (e->terms[1].type) { |
| case YASM_EXPR_REG: |
| case YASM_EXPR_FLOAT: |
| return 1; /* not legal */ |
| case YASM_EXPR_SYM: |
| case YASM_EXPR_SYMEXP: |
| return 1; |
| case YASM_EXPR_EXPR: |
| if (value_finalize_scan(value, e->terms[1].data.expn, 1)) |
| return 1; |
| break; |
| default: |
| break; |
| } |
| |
| /* Check for single sym and allowed cases on LHS */ |
| switch (e->terms[0].type) { |
| /*case YASM_EXPR_REG: ????? should this be illegal ????? */ |
| case YASM_EXPR_FLOAT: |
| return 1; /* not legal */ |
| case YASM_EXPR_SYM: |
| case YASM_EXPR_SYMEXP: |
| if (value->rel || ssym_not_ok) |
| return 1; |
| value->rel = e->terms[0].data.sym; |
| /* and replace with 0 */ |
| e->terms[0].type = YASM_EXPR_INT; |
| e->terms[0].data.intn = yasm_intnum_create_uint(0); |
| break; |
| case YASM_EXPR_EXPR: |
| /* recurse */ |
| if (value_finalize_scan(value, e->terms[0].data.expn, |
| ssym_not_ok)) |
| return 1; |
| break; |
| default: |
| break; /* ignore */ |
| } |
| |
| /* Handle RHS */ |
| if (!value->rel) |
| break; /* no handling needed */ |
| if (e->terms[1].type != YASM_EXPR_INT) |
| return 1; /* can't shift sym by non-constant integer */ |
| shamt = yasm_intnum_get_uint(e->terms[1].data.intn); |
| if ((shamt + value->rshift) > YASM_VALUE_RSHIFT_MAX) |
| return 1; /* total shift would be too large */ |
| value->rshift += shamt; |
| |
| /* Just leave SHR in place */ |
| break; |
| case YASM_EXPR_SEG: |
| /* Okay for single symrec (can only be done once). |
| * Not okay for anything BUT a single symrec as an immediate |
| * child. |
| */ |
| if (e->terms[0].type != YASM_EXPR_SYM |
| && e->terms[0].type != YASM_EXPR_SYMEXP) |
| return 1; |
| |
| if (value->seg_of) |
| return 1; /* multiple SEG not legal */ |
| value->seg_of = 1; |
| |
| if (value->rel || ssym_not_ok) |
| return 1; /* got a relative portion somewhere else? */ |
| value->rel = e->terms[0].data.sym; |
| |
| /* replace with ident'ed 0 */ |
| e->op = YASM_EXPR_IDENT; |
| e->terms[0].type = YASM_EXPR_INT; |
| e->terms[0].data.intn = yasm_intnum_create_uint(0); |
| break; |
| case YASM_EXPR_WRT: |
| /* Okay for single symrec in LHS and either a register or single |
| * symrec (as an immediate child) on RHS. |
| * If a single symrec on RHS, can only be done once. |
| * WRT reg is left in expr for arch to look at. |
| */ |
| |
| /* Handle RHS */ |
| switch (e->terms[1].type) { |
| case YASM_EXPR_SYM: |
| case YASM_EXPR_SYMEXP: |
| if (value->wrt) |
| return 1; |
| value->wrt = e->terms[1].data.sym; |
| /* and drop the WRT portion */ |
| e->op = YASM_EXPR_IDENT; |
| e->numterms = 1; |
| break; |
| case YASM_EXPR_REG: |
| break; /* ignore */ |
| default: |
| return 1; |
| } |
| |
| /* Handle LHS */ |
| switch (e->terms[0].type) { |
| case YASM_EXPR_SYM: |
| case YASM_EXPR_SYMEXP: |
| if (value->rel || ssym_not_ok) |
| return 1; |
| value->rel = e->terms[0].data.sym; |
| /* and replace with 0 */ |
| e->terms[0].type = YASM_EXPR_INT; |
| e->terms[0].data.intn = yasm_intnum_create_uint(0); |
| break; |
| case YASM_EXPR_EXPR: |
| /* recurse */ |
| return value_finalize_scan(value, e->terms[0].data.expn, |
| ssym_not_ok); |
| default: |
| break; /* ignore */ |
| } |
| |
| break; |
| default: |
| /* Single symrec not allowed anywhere */ |
| for (i=0; i<e->numterms; i++) { |
| switch (e->terms[i].type) { |
| case YASM_EXPR_SYM: |
| case YASM_EXPR_SYMEXP: |
| return 1; |
| case YASM_EXPR_EXPR: |
| /* recurse */ |
| return value_finalize_scan(value, |
| e->terms[i].data.expn, 1); |
| default: |
| break; |
| } |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int |
| yasm_value_finalize_expr(yasm_value *value, yasm_expr *e) |
| { |
| int error = 0; |
| |
| if (!e) { |
| yasm_value_initialize(value, NULL); |
| return 0; |
| } |
| |
| yasm_value_initialize(value, yasm_expr__level_tree |
| (e, 1, 1, 0, NULL, NULL, NULL, NULL, &error)); |
| |
| /* quit early if there was an issue in simplify() */ |
| if (error) |
| return 1; |
| |
| /* Handle trivial (IDENT) cases immediately */ |
| if (value->abs->op == YASM_EXPR_IDENT) { |
| switch (value->abs->terms[0].type) { |
| case YASM_EXPR_INT: |
| if (yasm_intnum_is_zero(value->abs->terms[0].data.intn)) { |
| yasm_expr_destroy(value->abs); |
| value->abs = NULL; |
| } |
| return 0; |
| case YASM_EXPR_REG: |
| case YASM_EXPR_FLOAT: |
| return 0; |
| case YASM_EXPR_SYM: |
| case YASM_EXPR_SYMEXP: |
| value->rel = value->abs->terms[0].data.sym; |
| yasm_expr_destroy(value->abs); |
| value->abs = NULL; |
| return 0; |
| case YASM_EXPR_EXPR: |
| /* Bring up lower values. */ |
| while (value->abs->op == YASM_EXPR_IDENT |
| && value->abs->terms[0].type == YASM_EXPR_EXPR) { |
| yasm_expr *sube = value->abs->terms[0].data.expn; |
| yasm_xfree(value->abs); |
| value->abs = sube; |
| } |
| break; |
| default: |
| yasm_internal_error(N_("unexpected expr term type")); |
| } |
| } |
| |
| if (value_finalize_scan(value, value->abs, 0)) |
| return 1; |
| |
| value->abs = yasm_expr__level_tree(value->abs, 1, 1, 0, NULL, NULL, NULL, |
| NULL, NULL); |
| |
| /* Simplify 0 in abs to NULL */ |
| if (value->abs->op == YASM_EXPR_IDENT |
| && value->abs->terms[0].type == YASM_EXPR_INT |
| && yasm_intnum_is_zero(value->abs->terms[0].data.intn)) { |
| yasm_expr_destroy(value->abs); |
| value->abs = NULL; |
| } |
| return 0; |
| } |
| |
| int |
| yasm_value_output_basic(yasm_value *value, /*@out@*/ unsigned char *buf, |
| size_t destsize, size_t valsize, int shift, |
| yasm_bytecode *bc, int warn, yasm_arch *arch, |
| yasm_calc_bc_dist_func calc_bc_dist) |
| { |
| /*@dependent@*/ /*@null@*/ yasm_intnum *intn = NULL; |
| /*@only@*/ yasm_intnum *outval; |
| int sym_local; |
| int retval = 1; |
| |
| if (value->abs) { |
| /* Handle floating point expressions */ |
| if (!value->rel && value->abs->op == YASM_EXPR_IDENT |
| && value->abs->terms[0].type == YASM_EXPR_FLOAT) { |
| if (shift < 0) |
| yasm_internal_error(N_("attempting to negative shift a float")); |
| if (yasm_arch_floatnum_tobytes(arch, value->abs->terms[0].data.flt, |
| buf, destsize, valsize, |
| (unsigned int)shift, warn, |
| bc->line)) |
| return -1; |
| else |
| return 1; |
| } |
| |
| /* Check for complex float expressions */ |
| if (yasm_expr__contains(value->abs, YASM_EXPR_FLOAT)) { |
| yasm__error(bc->line, N_("floating point expression too complex")); |
| return -1; |
| } |
| |
| /* Handle integer expressions */ |
| intn = yasm_expr_get_intnum(&value->abs, calc_bc_dist); |
| if (!intn) { |
| yasm__error(bc->line, N_("expression too complex")); |
| return -1; |
| } |
| } |
| |
| if (value->rel) { |
| /* If relative portion is not in bc section, don't try to handle it |
| * here. Otherwise get the relative portion's offset. |
| */ |
| /*@dependent@*/ yasm_bytecode *rel_prevbc; |
| unsigned long dist; |
| |
| sym_local = yasm_symrec_get_label(value->rel, &rel_prevbc); |
| if (value->wrt || value->seg_of || value->section_rel || !sym_local) |
| return 0; /* we can't handle SEG, WRT, or external symbols */ |
| if (rel_prevbc->section != bc->section) |
| return 0; /* not in this section */ |
| if (!value->curpos_rel) |
| return 0; /* not PC-relative */ |
| |
| /* Calculate value relative to current assembly position */ |
| dist = rel_prevbc->offset + rel_prevbc->len; |
| if (dist < bc->offset) { |
| outval = yasm_intnum_create_uint(bc->offset - dist); |
| yasm_intnum_calc(outval, YASM_EXPR_NEG, NULL, bc->line); |
| } else { |
| dist -= bc->offset; |
| outval = yasm_intnum_create_uint(dist); |
| } |
| |
| if (value->rshift > 0) { |
| /*@only@*/ yasm_intnum *shamt = |
| yasm_intnum_create_uint((unsigned long)value->rshift); |
| yasm_intnum_calc(outval, YASM_EXPR_SHR, shamt, bc->line); |
| yasm_intnum_destroy(shamt); |
| } |
| /* Add in absolute portion */ |
| if (intn) |
| yasm_intnum_calc(outval, YASM_EXPR_ADD, intn, bc->line); |
| /* Output! */ |
| if (yasm_arch_intnum_tobytes(arch, outval, buf, destsize, valsize, |
| shift, bc, warn, bc->line)) |
| retval = -1; |
| yasm_intnum_destroy(outval); |
| } else if (intn) { |
| /* Output just absolute portion */ |
| if (yasm_arch_intnum_tobytes(arch, intn, buf, destsize, valsize, |
| shift, bc, warn, bc->line)) |
| retval = -1; |
| } else { |
| /* No absolute or relative portions: output 0 */ |
| outval = yasm_intnum_create_uint(0); |
| if (yasm_arch_intnum_tobytes(arch, outval, buf, destsize, valsize, |
| shift, bc, warn, bc->line)) |
| retval = -1; |
| yasm_intnum_destroy(outval); |
| } |
| return retval; |
| } |
| |
| void |
| yasm_value_print(const yasm_value *value, FILE *f, int indent_level) |
| { |
| fprintf(f, "%*sAbsolute portion=", indent_level, ""); |
| yasm_expr_print(value->abs, f); |
| fprintf(f, "\n"); |
| if (value->rel) { |
| fprintf(f, "%*sRelative to=%s%s\n", indent_level, "", |
| value->seg_of ? "SEG " : "", |
| yasm_symrec_get_name(value->rel)); |
| if (value->wrt) |
| fprintf(f, "%*s(With respect to=%s)\n", indent_level, "", |
| yasm_symrec_get_name(value->wrt)); |
| if (value->rshift > 0) |
| fprintf(f, "%*s(Right shifted by=%u)\n", indent_level, "", |
| value->rshift); |
| if (value->curpos_rel) |
| fprintf(f, "%*s(Relative to current position)\n", indent_level, |
| ""); |
| } |
| } |