mirror of https://github.com/yasm/yasm.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
538 lines
16 KiB
538 lines
16 KiB
/* |
|
* 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, |
|
""); |
|
} |
|
}
|
|
|