| /* |
| * Value handling |
| * |
| * Copyright (C) 2006-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" |
| |
| #include "libyasm-stdint.h" |
| #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" |
| |
| |
| void |
| yasm_value_initialize(/*@out@*/ yasm_value *value, |
| /*@null@*/ /*@kept@*/ yasm_expr *e, unsigned int size) |
| { |
| value->abs = e; |
| value->rel = NULL; |
| value->wrt = NULL; |
| value->seg_of = 0; |
| value->rshift = 0; |
| value->curpos_rel = 0; |
| value->ip_rel = 0; |
| value->jump_target = 0; |
| value->section_rel = 0; |
| value->no_warn = 0; |
| value->sign = 0; |
| value->size = size; |
| } |
| |
| void |
| yasm_value_init_sym(/*@out@*/ yasm_value *value, /*@null@*/ yasm_symrec *sym, |
| unsigned int size) |
| { |
| value->abs = NULL; |
| value->rel = sym; |
| value->wrt = NULL; |
| value->seg_of = 0; |
| value->rshift = 0; |
| value->curpos_rel = 0; |
| value->ip_rel = 0; |
| value->jump_target = 0; |
| value->section_rel = 0; |
| value->no_warn = 0; |
| value->sign = 0; |
| value->size = size; |
| } |
| |
| void |
| yasm_value_init_copy(yasm_value *value, const yasm_value *orig) |
| { |
| value->abs = orig->abs ? yasm_expr_copy(orig->abs) : NULL; |
| value->rel = orig->rel; |
| value->wrt = orig->wrt; |
| value->seg_of = orig->seg_of; |
| value->rshift = orig->rshift; |
| value->curpos_rel = orig->curpos_rel; |
| value->ip_rel = orig->ip_rel; |
| value->jump_target = orig->jump_target; |
| value->section_rel = orig->section_rel; |
| value->no_warn = orig->no_warn; |
| value->sign = orig->sign; |
| value->size = orig->size; |
| } |
| |
| void |
| yasm_value_delete(yasm_value *value) |
| { |
| if (value->abs) |
| yasm_expr_destroy(value->abs); |
| value->abs = NULL; |
| value->rel = NULL; |
| } |
| |
| void |
| yasm_value_set_curpos_rel(yasm_value *value, yasm_bytecode *bc, |
| unsigned int ip_rel) |
| { |
| value->curpos_rel = 1; |
| value->ip_rel = ip_rel; |
| /* In order for us to correctly output curpos-relative values, we must |
| * have a relative portion of the value. If one doesn't exist, point |
| * to a custom absolute symbol. |
| */ |
| if (!value->rel) { |
| yasm_object *object = yasm_section_get_object(yasm_bc_get_section(bc)); |
| value->rel = yasm_symtab_abs_sym(object->symtab); |
| } |
| } |
| |
| static int |
| value_finalize_scan(yasm_value *value, yasm_expr *e, |
| /*@null@*/ yasm_bytecode *expr_precbc, 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, expr_precbc, |
| ssym_not_ok)) |
| return 1; |
| continue; |
| } |
| |
| if (sube->terms[0].type == YASM_EXPR_INT && |
| sube->terms[1].type == YASM_EXPR_SYM) { |
| intn = sube->terms[0].data.intn; |
| sym = sube->terms[1].data.sym; |
| } else if (sube->terms[0].type == YASM_EXPR_SYM && |
| 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, expr_precbc, |
| ssym_not_ok)) |
| return 1; |
| continue; |
| } |
| |
| if (!yasm_intnum_is_neg1(intn)) { |
| if (value_finalize_scan(value, sube, expr_precbc, |
| ssym_not_ok)) |
| return 1; |
| continue; |
| } |
| |
| /* Look for the same symrec term; even if both are external, |
| * they should cancel out. |
| */ |
| for (j=0; j<e->numterms; j++) { |
| if (e->terms[j].type == YASM_EXPR_SYM |
| && e->terms[j].data.sym == sym |
| && (used & (1<<j)) == 0) { |
| /* Mark as used */ |
| used |= 1<<j; |
| |
| /* 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) |
| continue; |
| |
| if (!yasm_symrec_get_label(sym, &precbc)) { |
| if (value_finalize_scan(value, sube, expr_precbc, |
| 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 |
| && 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. |
| * |
| * Similarly, handle -1*symrec in other segment via the |
| * following transformation: |
| * other-this = (other-.)+(.-this) |
| * We can only do this transformation if "this" is in |
| * this expr's segment. |
| * |
| * Don't do this if we've already become curpos-relative. |
| * The unmatched symrec will be caught below. |
| */ |
| if (j == e->numterms && !value->curpos_rel |
| && (yasm_symrec_is_curpos(sym) |
| || (expr_precbc |
| && sect2 == yasm_bc_get_section(expr_precbc)))) { |
| for (j=0; j<e->numterms; j++) { |
| if (e->terms[j].type == YASM_EXPR_SYM |
| && !yasm_symrec_get_equ(e->terms[j].data.sym) |
| && !yasm_symrec_is_special(e->terms[j].data.sym) |
| && (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; |
| if (yasm_symrec_is_curpos(sym)) { |
| /* 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); |
| } else { |
| /* Replace positive portion with curpos */ |
| yasm_object *object = |
| yasm_section_get_object(sect2); |
| yasm_symtab *symtab = object->symtab; |
| e->terms[j].data.sym = |
| yasm_symtab_define_curpos |
| (symtab, ".", expr_precbc, e->line); |
| } |
| 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 |
| && (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 single sym on LHS */ |
| if (e->terms[0].type != YASM_EXPR_SYM) |
| break; |
| |
| /* If we already have a sym, we can't take another one */ |
| if (value->rel || ssym_not_ok) |
| return 1; |
| |
| /* RHS must be a positive integer */ |
| 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 */ |
| |
| /* Update value */ |
| value->rshift += shamt; |
| value->rel = e->terms[0].data.sym; |
| |
| /* Replace symbol with 0 */ |
| e->terms[0].type = YASM_EXPR_INT; |
| e->terms[0].data.intn = yasm_intnum_create_uint(0); |
| |
| /* 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) |
| 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: |
| 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: |
| 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, |
| expr_precbc, 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: |
| return 1; |
| case YASM_EXPR_EXPR: |
| /* recurse */ |
| return value_finalize_scan(value, |
| e->terms[i].data.expn, |
| expr_precbc, 1); |
| default: |
| break; |
| } |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int |
| yasm_value_finalize_expr(yasm_value *value, yasm_expr *e, |
| yasm_bytecode *precbc, unsigned int size) |
| { |
| if (!e) { |
| yasm_value_initialize(value, NULL, size); |
| return 0; |
| } |
| yasm_value_initialize(value, e, size); |
| return yasm_value_finalize(value, precbc); |
| } |
| |
| int |
| yasm_value_finalize(yasm_value *value, yasm_bytecode *precbc) |
| { |
| if (!value->abs) |
| return 0; |
| |
| value->abs = yasm_expr__level_tree(value->abs, 1, 1, 0, 0, NULL, NULL); |
| |
| /* quit early if there was an issue in simplify() */ |
| if (yasm_error_occurred()) |
| return 1; |
| |
| /* Strip top-level AND masking to an all-1s mask the same size |
| * of the value size. This allows forced avoidance of overflow warnings. |
| */ |
| if (value->abs->op == YASM_EXPR_AND) { |
| int term; |
| |
| /* Calculate 1<<size - 1 value */ |
| yasm_intnum *mask = yasm_intnum_create_uint(1); |
| yasm_intnum *mask_tmp = yasm_intnum_create_uint(value->size); |
| yasm_intnum_calc(mask, YASM_EXPR_SHL, mask_tmp); |
| yasm_intnum_set_uint(mask_tmp, 1); |
| yasm_intnum_calc(mask, YASM_EXPR_SUB, mask_tmp); |
| yasm_intnum_destroy(mask_tmp); |
| |
| /* Walk terms and delete matching masks */ |
| for (term=value->abs->numterms-1; term>=0; term--) { |
| if (value->abs->terms[term].type == YASM_EXPR_INT && |
| yasm_intnum_compare(value->abs->terms[term].data.intn, |
| mask) == 0) { |
| /* Delete the intnum */ |
| yasm_intnum_destroy(value->abs->terms[term].data.intn); |
| |
| /* Slide everything to its right over by 1 */ |
| if (term != value->abs->numterms-1) /* if it wasn't last.. */ |
| memmove(&value->abs->terms[term], |
| &value->abs->terms[term+1], |
| (value->abs->numterms-1-term)* |
| sizeof(yasm_expr__item)); |
| |
| /* Update numterms */ |
| value->abs->numterms--; |
| |
| /* Indicate warnings have been disabled */ |
| value->no_warn = 1; |
| } |
| } |
| if (value->abs->numterms == 1) |
| value->abs->op = YASM_EXPR_IDENT; |
| yasm_intnum_destroy(mask); |
| } |
| |
| /* 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: |
| 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, precbc, 0)) |
| return 1; |
| |
| value->abs = yasm_expr__level_tree(value->abs, 1, 1, 0, 0, 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; |
| } |
| |
| yasm_intnum * |
| yasm_value_get_intnum(yasm_value *value, yasm_bytecode *bc, int calc_bc_dist) |
| { |
| /*@dependent@*/ /*@null@*/ yasm_intnum *intn = NULL; |
| /*@only@*/ yasm_intnum *outval; |
| int sym_local; |
| |
| if (value->abs) { |
| /* Handle integer expressions, if non-integer or too complex, return |
| * NULL. |
| */ |
| intn = yasm_expr_get_intnum(&value->abs, calc_bc_dist); |
| if (!intn) |
| return NULL; |
| } |
| |
| if (value->rel) { |
| /* If relative portion is not in bc section, return NULL. |
| * Otherwise get the relative portion's offset. |
| */ |
| /*@dependent@*/ yasm_bytecode *rel_prevbc; |
| unsigned long dist; |
| |
| if (!bc) |
| return NULL; /* Can't calculate relative value */ |
| |
| sym_local = yasm_symrec_get_label(value->rel, &rel_prevbc); |
| if (value->wrt || value->seg_of || value->section_rel || !sym_local) |
| return NULL; /* we can't handle SEG, WRT, or external symbols */ |
| if (rel_prevbc->section != bc->section) |
| return NULL; /* not in this section */ |
| if (!value->curpos_rel) |
| return NULL; /* not PC-relative */ |
| |
| /* Calculate value relative to current assembly position */ |
| dist = yasm_bc_next_offset(rel_prevbc); |
| if (dist < bc->offset) { |
| outval = yasm_intnum_create_uint(bc->offset - dist); |
| yasm_intnum_calc(outval, YASM_EXPR_NEG, NULL); |
| } 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); |
| yasm_intnum_destroy(shamt); |
| } |
| /* Add in absolute portion */ |
| if (intn) |
| yasm_intnum_calc(outval, YASM_EXPR_ADD, intn); |
| return outval; |
| } |
| |
| if (intn) |
| return yasm_intnum_copy(intn); |
| |
| /* No absolute or relative portions: output 0 */ |
| return yasm_intnum_create_uint(0); |
| } |
| |
| int |
| yasm_value_output_basic(yasm_value *value, /*@out@*/ unsigned char *buf, |
| size_t destsize, yasm_bytecode *bc, int warn, |
| yasm_arch *arch) |
| { |
| /*@dependent@*/ /*@null@*/ yasm_intnum *intn = NULL; |
| /*@only@*/ yasm_intnum *outval; |
| int sym_local; |
| int retval = 1; |
| unsigned int valsize = value->size; |
| |
| if (value->no_warn) |
| warn = 0; |
| |
| 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 (yasm_arch_floatnum_tobytes(arch, value->abs->terms[0].data.flt, |
| buf, destsize, valsize, 0, warn)) |
| return -1; |
| else |
| return 1; |
| } |
| |
| /* Check for complex float expressions */ |
| if (yasm_expr__contains(value->abs, YASM_EXPR_FLOAT)) { |
| yasm_error_set(YASM_ERROR_FLOATING_POINT, |
| N_("floating point expression too complex")); |
| return -1; |
| } |
| |
| /* Handle normal integer expressions */ |
| intn = yasm_expr_get_intnum(&value->abs, 1); |
| |
| if (!intn) { |
| /* Second try before erroring: yasm_expr_get_intnum doesn't handle |
| * SEG:OFF, so try simplifying out any to just the OFF portion, |
| * then getting the intnum again. |
| */ |
| yasm_expr *seg = yasm_expr_extract_deep_segoff(&value->abs); |
| if (seg) |
| yasm_expr_destroy(seg); |
| intn = yasm_expr_get_intnum(&value->abs, 1); |
| } |
| |
| if (!intn) { |
| /* Still don't have an integer! */ |
| yasm_error_set(YASM_ERROR_TOO_COMPLEX, |
| N_("expression too complex")); |
| return -1; |
| } |
| } |
| |
| /* Adjust warn for signed/unsigned integer warnings */ |
| if (warn != 0) |
| warn = value->sign ? -1 : 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 = yasm_bc_next_offset(rel_prevbc); |
| if (dist < bc->offset) { |
| outval = yasm_intnum_create_uint(bc->offset - dist); |
| yasm_intnum_calc(outval, YASM_EXPR_NEG, NULL); |
| } 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); |
| yasm_intnum_destroy(shamt); |
| } |
| /* Add in absolute portion */ |
| if (intn) |
| yasm_intnum_calc(outval, YASM_EXPR_ADD, intn); |
| /* Output! */ |
| if (yasm_arch_intnum_tobytes(arch, outval, buf, destsize, valsize, 0, |
| bc, warn)) |
| retval = -1; |
| yasm_intnum_destroy(outval); |
| return retval; |
| } |
| |
| if (value->seg_of || value->rshift || value->curpos_rel || value->ip_rel |
| || value->section_rel) |
| return 0; /* We can't handle this with just an absolute */ |
| |
| if (intn) { |
| /* Output just absolute portion */ |
| if (yasm_arch_intnum_tobytes(arch, intn, buf, destsize, valsize, 0, bc, |
| warn)) |
| 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, 0, |
| bc, warn)) |
| retval = -1; |
| yasm_intnum_destroy(outval); |
| } |
| return retval; |
| } |
| |
| void |
| yasm_value_print(const yasm_value *value, FILE *f, int indent_level) |
| { |
| fprintf(f, "%*s%u-bit, %ssigned", indent_level, "", value->size, |
| value->sign ? "" : "un"); |
| 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, |
| ""); |
| if (value->ip_rel) |
| fprintf(f, "%*s(IP-relative)\n", indent_level, ""); |
| if (value->jump_target) |
| fprintf(f, "%*s(Jump target)\n", indent_level, ""); |
| if (value->section_rel) |
| fprintf(f, "%*s(Section-relative)\n", indent_level, ""); |
| if (value->no_warn) |
| fprintf(f, "%*s(Overflow warnings disabled)\n", indent_level, ""); |
| } |
| } |