static int lc3b_bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span, void *add_span_data) { lc3b_insn *insn = (lc3b_insn *)bc->contents; yasm_bytecode *target_prevbc; /* Fixed size instruction length */ bc->len += 2; /* Only need to worry about out-of-range to PC-relative */ if (insn->imm_type != LC3B_IMM_9_PC) return 0; if (insn->imm.rel && (!yasm_symrec_get_label(insn->imm.rel, &target_prevbc) || target_prevbc->section != bc->section)) { /* External or out of segment, so we can't check distance. */ return 0; } /* 9-bit signed, word-multiple displacement */ add_span(add_span_data, bc, 1, &insn->imm, -512+(long)bc->len, 511+(long)bc->len); return 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; }
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); }
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; }
static int xdf_objfmt_output_sym(yasm_symrec *sym, /*@null@*/ void *d) { /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; yasm_sym_vis vis = yasm_symrec_get_visibility(sym); assert(info != NULL); if (info->all_syms || vis != YASM_SYM_LOCAL) { /*@only@*/ char *name = yasm_symrec_get_global_name(sym, info->object); const yasm_expr *equ_val; const yasm_intnum *intn; size_t len = strlen(name); unsigned long value = 0; long scnum = -3; /* -3 = debugging symbol */ /*@dependent@*/ /*@null@*/ yasm_section *sect; /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; unsigned long flags = 0; unsigned char *localbuf; if (vis & YASM_SYM_GLOBAL) flags = XDF_SYM_GLOBAL; /* Look at symrec for value/scnum/etc. */ if (yasm_symrec_get_label(sym, &precbc)) { if (precbc) sect = yasm_bc_get_section(precbc); else sect = NULL; /* it's a label: get value and offset. * If there is not a section, leave as debugging symbol. */ if (sect) { /*@dependent@*/ /*@null@*/ xdf_section_data *csectd; csectd = yasm_section_get_data(sect, &xdf_section_data_cb); if (csectd) scnum = csectd->scnum; else yasm_internal_error(N_("didn't understand section")); if (precbc) value += yasm_bc_next_offset(precbc); } } else if ((equ_val = yasm_symrec_get_equ(sym))) { yasm_expr *equ_val_copy = yasm_expr_copy(equ_val); intn = yasm_expr_get_intnum(&equ_val_copy, 1); if (!intn) { if (vis & YASM_SYM_GLOBAL) { yasm_error_set(YASM_ERROR_NOT_CONSTANT, N_("global EQU value not an integer expression")); yasm_errwarn_propagate(info->errwarns, equ_val->line); } } else value = yasm_intnum_get_uint(intn); yasm_expr_destroy(equ_val_copy); flags |= XDF_SYM_EQU; scnum = -2; /* -2 = absolute symbol */ } else { if (vis & YASM_SYM_EXTERN) { flags = XDF_SYM_EXTERN; scnum = -1; } } localbuf = info->buf; YASM_WRITE_32_L(localbuf, scnum); /* section number */ YASM_WRITE_32_L(localbuf, value); /* value */ YASM_WRITE_32_L(localbuf, info->strtab_offset); info->strtab_offset += (unsigned long)(len+1); YASM_WRITE_32_L(localbuf, flags); /* flags */ fwrite(info->buf, 16, 1, info->f); yasm_xfree(name); } return 0; }
/* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into single * expritems if possible. Uses a simple n^2 algorithm because n is usually * quite small. Also works for precbc-precbc (or symrec-precbc, * precbc-symrec). */ static /*@only@*/ yasm_expr * expr_xform_bc_dist_base(/*@returned@*/ /*@only@*/ yasm_expr *e, /*@null@*/ void *cbd, int (*callback) (yasm_expr__item *ei, yasm_bytecode *precbc, yasm_bytecode *precbc2, void *cbd)) { int i; /*@dependent@*/ yasm_section *sect; /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; int numterms; /* Handle symrec-symrec in ADD exprs by looking for (-1*symrec) and * symrec term pairs (where both symrecs are in the same segment). */ if (e->op != YASM_EXPR_ADD) return e; for (i=0; i<e->numterms; i++) { int j; yasm_expr *sube; yasm_intnum *intn; yasm_symrec *sym = NULL; /*@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) continue; if (sube->terms[0].type == YASM_EXPR_INT && (sube->terms[1].type == YASM_EXPR_SYM || sube->terms[1].type == YASM_EXPR_PRECBC)) { intn = sube->terms[0].data.intn; if (sube->terms[1].type == YASM_EXPR_PRECBC) precbc = sube->terms[1].data.precbc; else sym = sube->terms[1].data.sym; } else if ((sube->terms[0].type == YASM_EXPR_SYM || sube->terms[0].type == YASM_EXPR_PRECBC) && sube->terms[1].type == YASM_EXPR_INT) { if (sube->terms[0].type == YASM_EXPR_PRECBC) precbc = sube->terms[0].data.precbc; else sym = sube->terms[0].data.sym; intn = sube->terms[1].data.intn; } else continue; if (!yasm_intnum_is_neg1(intn)) continue; if (sym && !yasm_symrec_get_label(sym, &precbc)) continue; sect2 = yasm_bc_get_section(precbc); /* Now look for a 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)) || (e->terms[j].type == YASM_EXPR_PRECBC && (precbc2 = e->terms[j].data.precbc))) && (sect = yasm_bc_get_section(precbc2)) && sect == sect2 && callback(&e->terms[j], precbc, precbc2, cbd)) { /* Delete the matching (-1*symrec) term */ yasm_expr_destroy(sube); e->terms[i].type = YASM_EXPR_NONE; break; /* stop looking for matching symrec term */ } } } /* Clean up any deleted (EXPR_NONE) terms */ numterms = 0; for (i=0; i<e->numterms; i++) { if (e->terms[i].type != YASM_EXPR_NONE) e->terms[numterms++] = e->terms[i]; /* structure copy */ } if (e->numterms != numterms) { e->numterms = numterms; e = yasm_xrealloc(e, sizeof(yasm_expr)+((numterms<2) ? 0 : sizeof(yasm_expr__item)*(numterms-2))); if (numterms == 1) e->op = YASM_EXPR_IDENT; } return e; }