void define_common(char *label, int32_t segment, int32_t size, char *special) { union label *lptr; lptr = find_label(label, 1); if (!lptr) return; if ((lptr->defn.is_global & DEFINED_BIT) && (passn == 1 || !(lptr->defn.is_global & COMMON_BIT))) { nasm_error(ERR_NONFATAL, "symbol `%s' redefined", label); return; } lptr->defn.is_global |= DEFINED_BIT|COMMON_BIT; if (!islocalchar(label[0])) { prevlabel = lptr->defn.label; } else { nasm_error(ERR_NONFATAL, "attempt to define a local label as a " "common variable"); return; } lptr->defn.segment = segment; lptr->defn.offset = 0; if (pass0 == 0) return; ofmt->symdef(lptr->defn.label, segment, size, 2, special ? special : lptr->defn.special); ofmt->current_dfmt->debug_deflabel(lptr->defn.label, segment, size, 2, special ? special : lptr->defn.special); }
/* * The SEG operator: calculate the segment part of a relocatable * value. Return NULL, as usual, if an error occurs. Report the * error too. */ static expr *segment_part(expr * e) { int32_t seg; if (is_unknown(e)) return unknown_expr(); if (!is_reloc(e)) { nasm_error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value"); return NULL; } seg = reloc_seg(e); if (seg == NO_SEG) { nasm_error(ERR_NONFATAL, "cannot apply SEG to a non-relocatable value"); return NULL; } else if (seg & SEG_ABS) { return scalarvect(seg & ~SEG_ABS); } else if (seg & 1) { nasm_error(ERR_NONFATAL, "SEG applied to something which" " is already a segment base"); return NULL; } else { int32_t base = ofmt->segbase(seg + 1); begintemp(); addtotemp((base == NO_SEG ? EXPR_UNKNOWN : EXPR_SEGBASE + base), 1L); return finishtemp(); } }
void declare_as_global(char *label, char *special) { union label *lptr; if (islocal(label)) { nasm_error(ERR_NONFATAL, "attempt to declare local symbol `%s' as" " global", label); return; } lptr = find_label(label, 1); if (!lptr) return; switch (lptr->defn.is_global & TYPE_MASK) { case NOT_DEFINED_YET: lptr->defn.is_global = GLOBAL_PLACEHOLDER; lptr->defn.special = special ? perm_copy(special) : NULL; break; case GLOBAL_PLACEHOLDER: /* already done: silently ignore */ case GLOBAL_SYMBOL: break; case LOCAL_SYMBOL: if (!(lptr->defn.is_global & EXTERN_BIT)) { nasm_error(ERR_WARNING, "symbol `%s': GLOBAL directive " "after symbol definition is an experimental feature", label); lptr->defn.is_global = GLOBAL_SYMBOL; } break; } }
static void as86_deflabel(char *name, int32_t segment, int64_t offset, int is_global, char *special) { bool is_start = false; struct Symbol *sym; if (special) nasm_error(ERR_NONFATAL, "as86 format does not support any" " special symbol types"); if (name[0] == '.' && name[1] == '.' && name[2] != '@') { if (strcmp(name, "..start")) { nasm_error(ERR_NONFATAL, "unrecognised special symbol `%s'", name); return; } else { is_start = true; } } sym = saa_wstruct(syms); sym->strpos = as86_add_string(name); sym->flags = 0; if (is_start) sym->flags = SYM_ENTRY; if (segment == NO_SEG) sym->flags |= SYM_ABSOLUTE, sym->segment = 0; else if (segment == stext.index) sym->segment = SECT_TEXT; else if (segment == sdata.index) sym->segment = SECT_DATA; else if (segment == bssindex) sym->segment = SECT_BSS; else { sym->flags |= SYM_IMPORT; sym->segment = 15; } if (is_global == 2) sym->segment = 3; /* already have IMPORT */ if (is_global && !(sym->flags & SYM_IMPORT)) sym->flags |= SYM_EXPORT; sym->value = offset; /* * define the references from external-symbol segment numbers * to these symbol records. */ if (segment != NO_SEG && segment != stext.index && segment != sdata.index && segment != bssindex) bsym = raa_write(bsym, segment, nsyms); nsyms++; }
/* parse section attributes */ void section_attrib(char *name, char *attr, int pass, uint32_t *flags_and, uint32_t *flags_or, uint64_t *align, int *type) { char *opt, *val, *next; opt = nasm_skip_spaces(attr); if (!opt || !*opt) return; while ((opt = nasm_opt_val(opt, &val, &next))) { if (!nasm_stricmp(opt, "align")) { *align = atoi(val); if (*align == 0) { *align = SHA_ANY; } else if (!is_power2(*align)) { nasm_error(ERR_NONFATAL, "section alignment %"PRId64" is not a power of two", *align); *align = SHA_ANY; } } else if (!nasm_stricmp(opt, "alloc")) { *flags_and |= SHF_ALLOC; *flags_or |= SHF_ALLOC; } else if (!nasm_stricmp(opt, "noalloc")) { *flags_and |= SHF_ALLOC; *flags_or &= ~SHF_ALLOC; } else if (!nasm_stricmp(opt, "exec")) { *flags_and |= SHF_EXECINSTR; *flags_or |= SHF_EXECINSTR; } else if (!nasm_stricmp(opt, "noexec")) { *flags_and |= SHF_EXECINSTR; *flags_or &= ~SHF_EXECINSTR; } else if (!nasm_stricmp(opt, "write")) { *flags_and |= SHF_WRITE; *flags_or |= SHF_WRITE; } else if (!nasm_stricmp(opt, "tls")) { *flags_and |= SHF_TLS; *flags_or |= SHF_TLS; } else if (!nasm_stricmp(opt, "nowrite")) { *flags_and |= SHF_WRITE; *flags_or &= ~SHF_WRITE; } else if (!nasm_stricmp(opt, "progbits")) { *type = SHT_PROGBITS; } else if (!nasm_stricmp(opt, "nobits")) { *type = SHT_NOBITS; } else if (pass == 1) { nasm_error(ERR_WARNING, "Unknown section attribute '%s' ignored on" " declaration of section `%s'", opt, name); } opt = next; } }
static expr *eval_floatize(enum floatize type) { uint8_t result[16], *p; /* Up to 128 bits */ static const struct { int bytes, start, len; } formats[] = { { 1, 0, 1 }, /* FLOAT_8 */ { 2, 0, 2 }, /* FLOAT_16 */ { 4, 0, 4 }, /* FLOAT_32 */ { 8, 0, 8 }, /* FLOAT_64 */ { 10, 0, 8 }, /* FLOAT_80M */ { 10, 8, 2 }, /* FLOAT_80E */ { 16, 0, 8 }, /* FLOAT_128L */ { 16, 8, 8 }, /* FLOAT_128H */ }; int sign = 1; int64_t val; int j; i = scan(scpriv, tokval); if (i != '(') { nasm_error(ERR_NONFATAL, "expecting `('"); return NULL; } i = scan(scpriv, tokval); if (i == '-' || i == '+') { sign = (i == '-') ? -1 : 1; i = scan(scpriv, tokval); } if (i != TOKEN_FLOAT) { nasm_error(ERR_NONFATAL, "expecting floating-point number"); return NULL; } if (!float_const(tokval->t_charptr, sign, result, formats[type].bytes)) return NULL; i = scan(scpriv, tokval); if (i != ')') { nasm_error(ERR_NONFATAL, "expecting `)'"); return NULL; } p = result+formats[type].start+formats[type].len; val = 0; for (j = formats[type].len; j; j--) { p--; val = (val << 8) + *p; } begintemp(); addtotemp(EXPR_SIMPLE, val); i = scan(scpriv, tokval); return finishtemp(); }
static expr *rexp1(int critical) { expr *e, *f; e = rexp2(critical); if (!e) return NULL; while (i == TOKEN_DBL_XOR) { i = scan(scpriv, tokval); f = rexp2(critical); if (!f) return NULL; if (!(is_simple(e) || is_just_unknown(e)) || !(is_simple(f) || is_just_unknown(f))) { nasm_error(ERR_NONFATAL, "`^' operator may only be applied to" " scalar values"); } if (is_just_unknown(e) || is_just_unknown(f)) e = unknown_expr(); else e = scalarvect((int64_t)(!reloc_value(e) ^ !reloc_value(f))); } return e; }
static expr *expr2(int critical) { expr *e, *f; e = expr3(critical); if (!e) return NULL; while (i == '&') { i = scan(scpriv, tokval); f = expr3(critical); if (!f) return NULL; if (!(is_simple(e) || is_just_unknown(e)) || !(is_simple(f) || is_just_unknown(f))) { nasm_error(ERR_NONFATAL, "`&' operator may only be applied to" " scalar values"); } if (is_just_unknown(e) || is_just_unknown(f)) e = unknown_expr(); else e = scalarvect(reloc_value(e) & reloc_value(f)); } return e; }
static int64_t eval_ifunc(int64_t val, enum ifunc func) { int errtype; uint64_t uval = (uint64_t)val; int64_t rv; switch (func) { case IFUNC_ILOG2E: case IFUNC_ILOG2W: errtype = (func == IFUNC_ILOG2E) ? ERR_NONFATAL : ERR_WARNING; if (!is_power2(uval)) nasm_error(errtype, "ilog2 argument is not a power of two"); /* fall through */ case IFUNC_ILOG2F: rv = ilog2_64(uval); break; case IFUNC_ILOG2C: rv = (uval < 2) ? 0 : ilog2_64(uval-1) + 1; break; default: nasm_panic(0, "invalid IFUNC token %d", func); rv = 0; break; } return rv; }
static expr *expr3(int critical) { expr *e, *f; e = expr4(critical); if (!e) return NULL; while (i == TOKEN_SHL || i == TOKEN_SHR) { int j = i; i = scan(scpriv, tokval); f = expr4(critical); if (!f) return NULL; if (!(is_simple(e) || is_just_unknown(e)) || !(is_simple(f) || is_just_unknown(f))) { nasm_error(ERR_NONFATAL, "shift operator may only be applied to" " scalar values"); } else if (is_just_unknown(e) || is_just_unknown(f)) { e = unknown_expr(); } else switch (j) { case TOKEN_SHL: e = scalarvect(reloc_value(e) << reloc_value(f)); break; case TOKEN_SHR: e = scalarvect(((uint64_t)reloc_value(e)) >> reloc_value(f)); break; } } return e; }
/* * because this routine is not bracketed in * the main program, this routine will be called even if there * is no request for debug info * so, we have to make sure the ??LINE segment is avaialbe * as the first segment when this debug format is selected */ static void dbgls_linnum(const char *lnfname, int32_t lineno, int32_t segto) { struct FileName *fn; struct ieeeSection *seg; int i = 0; if (segto == NO_SEG) return; /* * If `any_segs' is still false, we must define a default * segment. */ if (!any_segs) { int tempint; /* ignored */ if (segto != ieee_segment("__NASMDEFSEG", 2, &tempint)) nasm_error(ERR_PANIC, "strange segment conditions in OBJ driver"); } /* * Find the segment we are targetting. */ for (seg = seghead; seg; seg = seg->next) if (seg->index == segto) break; if (!seg) nasm_error(ERR_PANIC, "lineno directed to nonexistent segment?"); for (fn = fnhead; fn; fn = fn->next) { if (!nasm_stricmp(lnfname, fn->name)) break; i++; } if (!fn) { fn = nasm_malloc(sizeof(*fn)); fn->name = nasm_malloc(strlen(lnfname) + 1); fn->index = i; strcpy(fn->name, lnfname); fn->next = NULL; *fntail = fn; fntail = &fn->next; } ieee_write_byte(seghead, fn->index); ieee_write_word(seghead, lineno); ieee_write_fixup(segto, NO_SEG, seghead, 4, OUT_ADDRESS, seg->currentpos); }
static expr *rexp3(int critical) { expr *e, *f; int64_t v; e = expr0(critical); if (!e) return NULL; while (i == TOKEN_EQ || i == TOKEN_LT || i == TOKEN_GT || i == TOKEN_NE || i == TOKEN_LE || i == TOKEN_GE) { int j = i; i = scan(scpriv, tokval); f = expr0(critical); if (!f) return NULL; e = add_vectors(e, scalar_mult(f, -1L, false)); switch (j) { case TOKEN_EQ: case TOKEN_NE: if (is_unknown(e)) v = -1; /* means unknown */ else if (!is_really_simple(e) || reloc_value(e) != 0) v = (j == TOKEN_NE); /* unequal, so return true if NE */ else v = (j == TOKEN_EQ); /* equal, so return true if EQ */ break; default: if (is_unknown(e)) v = -1; /* means unknown */ else if (!is_really_simple(e)) { nasm_error(ERR_NONFATAL, "`%s': operands differ by a non-scalar", (j == TOKEN_LE ? "<=" : j == TOKEN_LT ? "<" : j == TOKEN_GE ? ">=" : ">")); v = 0; /* must set it to _something_ */ } else { int64_t vv = reloc_value(e); if (vv == 0) v = (j == TOKEN_LE || j == TOKEN_GE); else if (vv > 0) v = (j == TOKEN_GE || j == TOKEN_GT); else /* vv < 0 */ v = (j == TOKEN_LE || j == TOKEN_LT); } break; } if (v == -1) e = unknown_expr(); else e = scalarvect(v); } return e; }
static bool declare_label_lptr(union label *lptr, enum label_type type, const char *special) { if (special && !special[0]) special = NULL; if (lptr->defn.type == type || (pass0 == 0 && lptr->defn.type == LBL_LOCAL)) { lptr->defn.type = type; if (special) { if (!lptr->defn.special) lptr->defn.special = perm_copy(special); else if (nasm_stricmp(lptr->defn.special, special)) nasm_error(ERR_NONFATAL, "symbol `%s' has inconsistent attributes `%s' and `%s'", lptr->defn.label, lptr->defn.special, special); } return true; } /* EXTERN can be replaced with GLOBAL or COMMON */ if (lptr->defn.type == LBL_EXTERN && (type == LBL_GLOBAL || type == LBL_COMMON)) { lptr->defn.type = type; /* Override special unconditionally */ if (special) lptr->defn.special = perm_copy(special); return true; } /* GLOBAL or COMMON ignore subsequent EXTERN */ if ((lptr->defn.type == LBL_GLOBAL || lptr->defn.type == LBL_COMMON) && type == LBL_EXTERN) { if (!lptr->defn.special) lptr->defn.special = perm_copy(special); return false; /* Don't call define_label() after this! */ } nasm_error(ERR_NONFATAL, "symbol `%s' declared both as %s and %s", lptr->defn.label, types[lptr->defn.type], types[type]); return false; }
static expr *eval_strfunc(enum strfunc type) { char *string; size_t string_len; int64_t val; bool parens, rn_warn; parens = false; i = scan(scpriv, tokval); if (i == '(') { parens = true; i = scan(scpriv, tokval); } if (i != TOKEN_STR) { nasm_error(ERR_NONFATAL, "expecting string"); return NULL; } string_len = string_transform(tokval->t_charptr, tokval->t_inttwo, &string, type); if (string_len == (size_t)-1) { nasm_error(ERR_NONFATAL, "invalid string for transform"); return NULL; } val = readstrnum(string, string_len, &rn_warn); if (parens) { i = scan(scpriv, tokval); if (i != ')') { nasm_error(ERR_NONFATAL, "expecting `)'"); return NULL; } } if (rn_warn) nasm_error(ERR_WARNING|ERR_PASS1, "character constant too long"); begintemp(); addtotemp(EXPR_SIMPLE, val); i = scan(scpriv, tokval); return finishtemp(); }
/* * when two or more decorators follow a register operand, * consecutive decorators are parsed here. * opmask and zeroing decorators can be placed in any order. * e.g. zmm1 {k2}{z} or zmm2 {z}{k3} * decorator(s) are placed at the end of an operand. */ static bool parse_braces(decoflags_t *decoflags) { int i; bool recover = false; i = tokval.t_type; do { if (i == TOKEN_OPMASK) { if (*decoflags & OPMASK_MASK) { nasm_error(ERR_NONFATAL, "opmask k%"PRIu64" is already set", *decoflags & OPMASK_MASK); *decoflags &= ~OPMASK_MASK; } *decoflags |= VAL_OPMASK(nasm_regvals[tokval.t_integer]); } else if (i == TOKEN_DECORATOR) { switch (tokval.t_integer) { case BRC_Z: /* * according to AVX512 spec, only zeroing/merging decorator * is supported with opmask */ *decoflags |= GEN_Z(0); break; default: nasm_error(ERR_NONFATAL, "{%s} is not an expected decorator", tokval.t_charptr); break; } } else if (i == ',' || i == TOKEN_EOS){ break; } else { nasm_error(ERR_NONFATAL, "only a series of valid decorators" " expected"); recover = true; break; } i = stdscan(NULL, &tokval); } while(1); return recover; }
/* * Internal routine: finds the `union label' corresponding to the * given label name. Creates a new one, if it isn't found, and if * `create' is true. */ static union label *find_label(char *label, int create) { char *prev; int prevlen, len; union label *lptr, **lpp; char label_str[IDLEN_MAX]; struct hash_insert ip; if (islocal(label)) { prev = prevlabel; prevlen = strlen(prev); len = strlen(label); if (prevlen + len >= IDLEN_MAX) { nasm_error(ERR_NONFATAL, "identifier length exceed %i bytes", IDLEN_MAX); return NULL; } memcpy(label_str, prev, prevlen); memcpy(label_str+prevlen, label, len+1); label = label_str; } else { prev = ""; prevlen = 0; } lpp = (union label **) hash_find(<ab, label, &ip); lptr = lpp ? *lpp : NULL; if (lptr || !create) return lptr; /* Create a new label... */ if (lfree->admin.movingon == END_BLOCK) { /* * must allocate a new block */ lfree->admin.next = (union label *)nasm_malloc(LBLK_SIZE); lfree = lfree->admin.next; init_block(lfree); } lfree->admin.movingon = BOGUS_VALUE; lfree->defn.label = perm_copy(label); lfree->defn.special = NULL; lfree->defn.is_global = NOT_DEFINED_YET; hash_add(&ip, lfree->defn.label, lfree); return lfree++; }
/* * a token is enclosed with braces. proper token type will be assigned * accordingly with the token flag. */ static int stdscan_handle_brace(struct tokenval *tv) { if (!(tv->t_flag & TFLAG_BRC_ANY)) { /* invalid token is put inside braces */ nasm_error(ERR_NONFATAL, "%s is not a valid decorator with braces", tv->t_charptr); tv->t_type = TOKEN_INVALID; } else if (tv->t_flag & TFLAG_BRC_OPT) { if (is_reg_class(OPMASKREG, tv->t_integer)) { /* within braces, opmask register is now used as a mask */ tv->t_type = TOKEN_OPMASK; } } return tv->t_type; }
static void as86_set_rsize(int size) { if (as86_reloc_size != size) { switch (as86_reloc_size = size) { case 1: fputc(0x01, ofile); break; case 2: fputc(0x02, ofile); break; case 4: fputc(0x03, ofile); break; default: nasm_error(ERR_PANIC, "bizarre relocation size %d", size); break; } } }
static int prefix_slot(int prefix) { switch (prefix) { case P_WAIT: return PPS_WAIT; case R_CS: case R_DS: case R_SS: case R_ES: case R_FS: case R_GS: return PPS_SEG; case P_LOCK: return PPS_LOCK; case P_REP: case P_REPE: case P_REPZ: case P_REPNE: case P_REPNZ: case P_XACQUIRE: case P_XRELEASE: case P_BND: case P_NOBND: return PPS_REP; case P_O16: case P_O32: case P_O64: case P_OSP: return PPS_OSIZE; case P_A16: case P_A32: case P_A64: case P_ASP: return PPS_ASIZE; case P_EVEX: case P_VEX3: case P_VEX2: return PPS_VEX; default: nasm_error(ERR_PANIC, "Invalid value %d passed to prefix_slot()", prefix); return -1; } }
static void nop_reset(char *file, int pass, ListGen *listgen, StrList **deplist) { src_set_fname(nasm_strdup(file)); src_set_linnum(0); nop_lineinc = 1; nop_fp = fopen(file, "r"); if (!nop_fp) nasm_error(ERR_FATAL | ERR_NOFILE, "unable to open input file `%s'", file); nop_list = listgen; (void)pass; /* placate compilers */ if (deplist) { StrList *sl = nasm_malloc(strlen(file)+1+sizeof sl->next); sl->next = NULL; strcpy(sl->str, file); *deplist = sl; } }
/* * this routine is unalduterated bloatware. I usually don't do this * but I might as well see what it is like on a harmless program. * If anyone wants to optimize this is a good canditate! */ static void ieee_write_fixup(int32_t segment, int32_t wrt, struct ieeeSection *segto, int size, uint64_t realtype, int32_t offset) { struct ieeeSection *target; struct ieeeFixupp s; /* Don't put a fixup for things NASM can calculate */ if (wrt == NO_SEG && segment == NO_SEG) return; s.ftype = -1; /* if it is a WRT offset */ if (wrt != NO_SEG) { s.ftype = FT_WRT; s.addend = offset; if (wrt >= SEG_ABS) s.id1 = -(wrt - SEG_ABS); else { if (wrt % 2 && realtype != OUT_REL2ADR && realtype != OUT_REL4ADR) { wrt--; for (target = seghead; target; target = target->next) if (target->index == wrt) break; if (target) { s.id1 = target->ieee_index; for (target = seghead; target; target = target->next) if (target->index == segment) break; if (target) s.id2 = target->ieee_index; else { /* * Now we assume the segment field is being used * to hold an extern index */ int32_t i = segment / 2; struct ExtBack *eb = ebhead; while (i > EXT_BLKSIZ) { if (eb) eb = eb->next; else break; i -= EXT_BLKSIZ; } /* if we have an extern decide the type and make a record */ if (eb) { s.ftype = FT_EXTWRT; s.addend = 0; s.id2 = eb->index[i]; } else nasm_error(ERR_NONFATAL, "Source of WRT must be an offset"); } } else nasm_error(ERR_PANIC, "unrecognised WRT value in ieee_write_fixup"); } else nasm_error(ERR_NONFATAL, "target of WRT must be a section "); } s.size = size; ieee_install_fixup(segto, &s); return; } /* Pure segment fixup ? */ if (segment != NO_SEG) { s.ftype = FT_SEG; s.id1 = 0; if (segment >= SEG_ABS) { /* absolute far segment fixup */ s.id1 = -(segment - ~SEG_ABS); } else if (segment % 2) { /* fixup to named segment */ /* look it up */ for (target = seghead; target; target = target->next) if (target->index == segment - 1) break; if (target) s.id1 = target->ieee_index; else { /* * Now we assume the segment field is being used * to hold an extern index */ int32_t i = segment / 2; struct ExtBack *eb = ebhead; while (i > EXT_BLKSIZ) { if (eb) eb = eb->next; else break; i -= EXT_BLKSIZ; } /* if we have an extern decide the type and make a record */ if (eb) { if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) { nasm_error(ERR_PANIC, "Segment of a rel not supported in ieee_write_fixup"); } else { /* If we want the segment */ s.ftype = FT_EXTSEG; s.addend = 0; s.id1 = eb->index[i]; } } else /* If we get here the seg value doesn't make sense */ nasm_error(ERR_PANIC, "unrecognised segment value in ieee_write_fixup"); } } else { /* Assume we are offsetting directly from a section * So look up the target segment */ for (target = seghead; target; target = target->next) if (target->index == segment) break; if (target) { if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) { /* PC rel to a known offset */ s.id1 = target->ieee_index; s.ftype = FT_REL; s.size = size; s.addend = offset; } else { /* We were offsetting from a seg */ s.id1 = target->ieee_index; s.ftype = FT_OFS; s.size = size; s.addend = offset; } } else { /* * Now we assume the segment field is being used * to hold an extern index */ int32_t i = segment / 2; struct ExtBack *eb = ebhead; while (i > EXT_BLKSIZ) { if (eb) eb = eb->next; else break; i -= EXT_BLKSIZ; } /* if we have an extern decide the type and make a record */ if (eb) { if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) { s.ftype = FT_EXTREL; s.addend = 0; s.id1 = eb->index[i]; } else { /* else we want the external offset */ s.ftype = FT_EXT; s.addend = 0; s.id1 = eb->index[i]; } } else /* If we get here the seg value doesn't make sense */ nasm_error(ERR_PANIC, "unrecognised segment value in ieee_write_fixup"); } } if (size != 2 && s.ftype == FT_SEG) nasm_error(ERR_NONFATAL, "IEEE format can only handle 2-byte" " segment base references"); s.size = size; ieee_install_fixup(segto, &s); return; } /* should never get here */ }
/* * Put data out */ static void ieee_out(int32_t segto, const void *data, enum out_type type, uint64_t size, int32_t segment, int32_t wrt) { const uint8_t *ucdata; int32_t ldata; struct ieeeSection *seg; /* * handle absolute-assembly (structure definitions) */ if (segto == NO_SEG) { if (type != OUT_RESERVE) nasm_error(ERR_NONFATAL, "attempt to assemble code in [ABSOLUTE]" " space"); return; } /* * If `any_segs' is still false, we must define a default * segment. */ if (!any_segs) { int tempint; /* ignored */ if (segto != ieee_segment("__NASMDEFSEG", 2, &tempint)) nasm_error(ERR_PANIC, "strange segment conditions in IEEE driver"); } /* * Find the segment we are targetting. */ for (seg = seghead; seg; seg = seg->next) if (seg->index == segto) break; if (!seg) nasm_error(ERR_PANIC, "code directed to nonexistent segment?"); if (type == OUT_RAWDATA) { ucdata = data; while (size--) ieee_write_byte(seg, *ucdata++); } else if (type == OUT_ADDRESS || type == OUT_REL2ADR || type == OUT_REL4ADR) { if (segment == NO_SEG && type != OUT_ADDRESS) nasm_error(ERR_NONFATAL, "relative call to absolute address not" " supported by IEEE format"); ldata = *(int64_t *)data; if (type == OUT_REL2ADR) ldata += (size - 2); if (type == OUT_REL4ADR) ldata += (size - 4); ieee_write_fixup(segment, wrt, seg, size, type, ldata); if (size == 2) ieee_write_word(seg, ldata); else ieee_write_dword(seg, ldata); } else if (type == OUT_RESERVE) { while (size--) ieee_write_byte(seg, 0); } }
/* * callback for labels */ static void ieee_deflabel(char *name, int32_t segment, int64_t offset, int is_global, char *special) { /* * We have three cases: * * (i) `segment' is a segment-base. If so, set the name field * for the segment structure it refers to, and then * return. * * (ii) `segment' is one of our segments, or a SEG_ABS segment. * Save the label position for later output of a PUBDEF record. * * * (iii) `segment' is not one of our segments. Save the label * position for later output of an EXTDEF. */ struct ieeeExternal *ext; struct ExtBack *eb; struct ieeeSection *seg; int i; if (special) { nasm_error(ERR_NONFATAL, "unrecognised symbol type `%s'", special); } /* * First check for the double-period, signifying something * unusual. */ if (name[0] == '.' && name[1] == '.') { if (!strcmp(name, "..start")) { ieee_entry_seg = segment; ieee_entry_ofs = offset; } return; } /* * Case (i): */ if (ieee_seg_needs_update) { ieee_seg_needs_update->name = name; return; } if (segment < SEG_ABS && segment != NO_SEG && segment % 2) return; /* * case (ii) */ if (segment >= SEG_ABS) { /* * SEG_ABS subcase of (ii). */ if (is_global) { struct ieeePublic *pub; pub = *fpubtail = nasm_malloc(sizeof(*pub)); fpubtail = &pub->next; pub->next = NULL; pub->name = name; pub->offset = offset; pub->segment = segment & ~SEG_ABS; } return; } for (seg = seghead; seg && is_global; seg = seg->next) if (seg->index == segment) { struct ieeePublic *pub; last_defined = pub = *seg->pubtail = nasm_malloc(sizeof(*pub)); seg->pubtail = &pub->next; pub->next = NULL; pub->name = name; pub->offset = offset; pub->index = seg->ieee_index; pub->segment = -1; return; } /* * Case (iii). */ if (is_global) { ext = *exttail = nasm_malloc(sizeof(*ext)); ext->next = NULL; exttail = &ext->next; ext->name = name; if (is_global == 2) ext->commonsize = offset; else ext->commonsize = 0; i = segment / 2; eb = ebhead; if (!eb) { eb = *ebtail = nasm_malloc(sizeof(*eb)); eb->next = NULL; ebtail = &eb->next; } while (i > EXT_BLKSIZ) { if (eb && eb->next) eb = eb->next; else { eb = *ebtail = nasm_malloc(sizeof(*eb)); eb->next = NULL; ebtail = &eb->next; } i -= EXT_BLKSIZ; } eb->index[i] = externals++; } }
int stdscan(void *private_data, struct tokenval *tv) { char ourcopy[MAX_KEYWORD + 1], *r, *s; (void)private_data; /* Don't warn that this parameter is unused */ stdscan_bufptr = nasm_skip_spaces(stdscan_bufptr); if (!*stdscan_bufptr) return tv->t_type = TOKEN_EOS; /* we have a token; either an id, a number or a char */ if (isidstart(*stdscan_bufptr) || (*stdscan_bufptr == '$' && isidstart(stdscan_bufptr[1]))) { /* now we've got an identifier */ bool is_sym = false; int token_type; if (*stdscan_bufptr == '$') { is_sym = true; stdscan_bufptr++; } r = stdscan_bufptr++; /* read the entire buffer to advance the buffer pointer but... */ while (isidchar(*stdscan_bufptr)) stdscan_bufptr++; /* ... copy only up to IDLEN_MAX-1 characters */ tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r < IDLEN_MAX ? stdscan_bufptr - r : IDLEN_MAX - 1); if (is_sym || stdscan_bufptr - r > MAX_KEYWORD) return tv->t_type = TOKEN_ID; /* bypass all other checks */ for (s = tv->t_charptr, r = ourcopy; *s; s++) *r++ = nasm_tolower(*s); *r = '\0'; /* right, so we have an identifier sitting in temp storage. now, * is it actually a register or instruction name, or what? */ token_type = nasm_token_hash(ourcopy, tv); if (likely(!(tv->t_flag & TFLAG_BRC))) { /* most of the tokens fall into this case */ return token_type; } else { return tv->t_type = TOKEN_ID; } } else if (*stdscan_bufptr == '$' && !isnumchar(stdscan_bufptr[1])) { /* * It's a $ sign with no following hex number; this must * mean it's a Here token ($), evaluating to the current * assembly location, or a Base token ($$), evaluating to * the base of the current segment. */ stdscan_bufptr++; if (*stdscan_bufptr == '$') { stdscan_bufptr++; return tv->t_type = TOKEN_BASE; } return tv->t_type = TOKEN_HERE; } else if (isnumstart(*stdscan_bufptr)) { /* now we've got a number */ bool rn_error; bool is_hex = false; bool is_float = false; bool has_e = false; char c; r = stdscan_bufptr; if (*stdscan_bufptr == '$') { stdscan_bufptr++; is_hex = true; } for (;;) { c = *stdscan_bufptr++; if (!is_hex && (c == 'e' || c == 'E')) { has_e = true; if (*stdscan_bufptr == '+' || *stdscan_bufptr == '-') { /* * e can only be followed by +/- if it is either a * prefixed hex number or a floating-point number */ is_float = true; stdscan_bufptr++; } } else if (c == 'H' || c == 'h' || c == 'X' || c == 'x') { is_hex = true; } else if (c == 'P' || c == 'p') { is_float = true; if (*stdscan_bufptr == '+' || *stdscan_bufptr == '-') stdscan_bufptr++; } else if (isnumchar(c) || c == '_') ; /* just advance */ else if (c == '.') is_float = true; else break; } stdscan_bufptr--; /* Point to first character beyond number */ if (has_e && !is_hex) { /* 1e13 is floating-point, but 1e13h is not */ is_float = true; } if (is_float) { tv->t_charptr = stdscan_copy(r, stdscan_bufptr - r); return tv->t_type = TOKEN_FLOAT; } else { r = stdscan_copy(r, stdscan_bufptr - r); tv->t_integer = readnum(r, &rn_error); stdscan_pop(); if (rn_error) { /* some malformation occurred */ return tv->t_type = TOKEN_ERRNUM; } tv->t_charptr = NULL; return tv->t_type = TOKEN_NUM; } } else if (*stdscan_bufptr == '\'' || *stdscan_bufptr == '"' || *stdscan_bufptr == '`') { /* a quoted string */ char start_quote = *stdscan_bufptr; tv->t_charptr = stdscan_bufptr; tv->t_inttwo = nasm_unquote(tv->t_charptr, &stdscan_bufptr); if (*stdscan_bufptr != start_quote) return tv->t_type = TOKEN_ERRSTR; stdscan_bufptr++; /* Skip final quote */ return tv->t_type = TOKEN_STR; } else if (*stdscan_bufptr == '{') { /* now we've got a decorator */ int token_len; stdscan_bufptr = nasm_skip_spaces(stdscan_bufptr); r = ++stdscan_bufptr; /* * read the entire buffer to advance the buffer pointer * {rn-sae}, {rd-sae}, {ru-sae}, {rz-sae} contain '-' in tokens. */ while (isbrcchar(*stdscan_bufptr)) stdscan_bufptr++; token_len = stdscan_bufptr - r; /* ... copy only up to DECOLEN_MAX-1 characters */ tv->t_charptr = stdscan_copy(r, token_len < DECOLEN_MAX ? token_len : DECOLEN_MAX - 1); stdscan_bufptr = nasm_skip_spaces(stdscan_bufptr); /* if brace is not closed properly or token is too long */ if ((*stdscan_bufptr != '}') || (token_len > MAX_KEYWORD)) { nasm_error(ERR_NONFATAL, "invalid decorator token inside braces"); return tv->t_type = TOKEN_INVALID; } stdscan_bufptr++; /* skip closing brace */ for (s = tv->t_charptr, r = ourcopy; *s; s++) *r++ = nasm_tolower(*s); *r = '\0'; /* right, so we have a decorator sitting in temp storage. */ nasm_token_hash(ourcopy, tv); /* handle tokens inside braces */ return stdscan_handle_brace(tv); } else if (*stdscan_bufptr == ';') { /* a comment has happened - stay */ return tv->t_type = TOKEN_EOS; } else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '>') { stdscan_bufptr += 2; return tv->t_type = TOKEN_SHR; } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '<') { stdscan_bufptr += 2; return tv->t_type = TOKEN_SHL; } else if (stdscan_bufptr[0] == '/' && stdscan_bufptr[1] == '/') { stdscan_bufptr += 2; return tv->t_type = TOKEN_SDIV; } else if (stdscan_bufptr[0] == '%' && stdscan_bufptr[1] == '%') { stdscan_bufptr += 2; return tv->t_type = TOKEN_SMOD; } else if (stdscan_bufptr[0] == '=' && stdscan_bufptr[1] == '=') { stdscan_bufptr += 2; return tv->t_type = TOKEN_EQ; } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '>') { stdscan_bufptr += 2; return tv->t_type = TOKEN_NE; } else if (stdscan_bufptr[0] == '!' && stdscan_bufptr[1] == '=') { stdscan_bufptr += 2; return tv->t_type = TOKEN_NE; } else if (stdscan_bufptr[0] == '<' && stdscan_bufptr[1] == '=') { stdscan_bufptr += 2; return tv->t_type = TOKEN_LE; } else if (stdscan_bufptr[0] == '>' && stdscan_bufptr[1] == '=') { stdscan_bufptr += 2; return tv->t_type = TOKEN_GE; } else if (stdscan_bufptr[0] == '&' && stdscan_bufptr[1] == '&') { stdscan_bufptr += 2; return tv->t_type = TOKEN_DBL_AND; } else if (stdscan_bufptr[0] == '^' && stdscan_bufptr[1] == '^') { stdscan_bufptr += 2; return tv->t_type = TOKEN_DBL_XOR; } else if (stdscan_bufptr[0] == '|' && stdscan_bufptr[1] == '|') { stdscan_bufptr += 2; return tv->t_type = TOKEN_DBL_OR; } else /* just an ordinary char */ return tv->t_type = (uint8_t)(*stdscan_bufptr++); }
insn *parse_line(char *buffer, insn *result) { int pass = 1; /* This used to be an argument. it's hardcoded now because we always want it as 1 */ bool insn_is_label = false; struct eval_hints hints; int operand; int critical; bool first; bool recover; int j; restart_parse: first = true; result->forw_ref = false; stdscan_reset(); stdscan_set(buffer); i = stdscan(NULL, &tokval); result->label = NULL; /* Assume no label */ result->eops = NULL; /* must do this, whatever happens */ result->operands = 0; /* must initialize this */ /* Ignore blank lines */ if (i == TOKEN_EOS) { result->opcode = I_none; return result; } if (i != TOKEN_ID && i != TOKEN_INSN && i != TOKEN_PREFIX && (i != TOKEN_REG || !IS_SREG(tokval.t_integer))) { nasm_error(ERR_NONFATAL, "label or instruction expected at start of line"); result->opcode = I_none; return result; } if (i == TOKEN_ID || (insn_is_label && i == TOKEN_INSN)) { /* there's a label here */ first = false; result->label = tokval.t_charptr; i = stdscan(NULL, &tokval); if (i == ':') { /* skip over the optional colon */ i = stdscan(NULL, &tokval); } else if (i == 0) { nasm_error(ERR_WARNING | ERR_WARN_OL | ERR_PASS1, "label alone on a line without a colon might be in error"); } } /* Just a label here */ if (i == TOKEN_EOS) { result->opcode = I_none; return result; } for (j = 0; j < MAXPREFIX; j++) result->prefixes[j] = P_none; result->times = 1L; while (i == TOKEN_PREFIX || (i == TOKEN_REG && IS_SREG(tokval.t_integer))) { first = false; /* * Handle special case: the TIMES prefix. */ if (i == TOKEN_PREFIX && tokval.t_integer == P_TIMES) { expr *value; i = stdscan(NULL, &tokval); value = evaluate(stdscan, NULL, &tokval, NULL, pass0, nasm_error, NULL); i = tokval.t_type; if (!value) { /* but, error in evaluator */ result->opcode = I_none; /* unrecoverable parse error: */ return result; /* ignore this instruction */ } if (!is_simple(value)) { nasm_error(ERR_NONFATAL, "non-constant argument supplied to TIMES"); result->times = 1L; } else { result->times = value->value; if (value->value < 0 && pass0 == 2) { nasm_error(ERR_NONFATAL, "TIMES value %"PRId64" is negative", value->value); result->times = 0; } } } else { int slot = prefix_slot(tokval.t_integer); if (result->prefixes[slot]) { if (result->prefixes[slot] == tokval.t_integer) nasm_error(ERR_WARNING | ERR_PASS1, "instruction has redundant prefixes"); else nasm_error(ERR_NONFATAL, "instruction has conflicting prefixes"); } result->prefixes[slot] = tokval.t_integer; i = stdscan(NULL, &tokval); } } if (i != TOKEN_INSN) { int j; enum prefixes pfx; for (j = 0; j < MAXPREFIX; j++) { if ((pfx = result->prefixes[j]) != P_none) break; } if (i == 0 && pfx != P_none) { /* * Instruction prefixes are present, but no actual * instruction. This is allowed: at this point we * invent a notional instruction of RESB 0. */ result->opcode = I_RESB; result->operands = 1; result->oprs[0].type = IMMEDIATE; result->oprs[0].offset = 0L; result->oprs[0].segment = result->oprs[0].wrt = NO_SEG; return result; } else { nasm_error(ERR_NONFATAL, "parser: instruction expected"); result->opcode = I_none; return result; } } result->opcode = tokval.t_integer; result->condition = tokval.t_inttwo; /* * INCBIN cannot be satisfied with incorrectly * evaluated operands, since the correct values _must_ be known * on the first pass. Hence, even in pass one, we set the * `critical' flag on calling evaluate(), so that it will bomb * out on undefined symbols. */ if (result->opcode == I_INCBIN) { critical = (pass0 < 2 ? 1 : 2); } else critical = (pass == 2 ? 2 : 0); if (result->opcode == I_DB || result->opcode == I_DW || result->opcode == I_DD || result->opcode == I_DQ || result->opcode == I_DT || result->opcode == I_DO || result->opcode == I_DY || result->opcode == I_INCBIN) { extop *eop, **tail = &result->eops, **fixptr; int oper_num = 0; int32_t sign; result->eops_float = false; /* * Begin to read the DB/DW/DD/DQ/DT/DO/INCBIN operands. */ while (1) { i = stdscan(NULL, &tokval); if (i == TOKEN_EOS) break; else if (first && i == ':') { insn_is_label = true; goto restart_parse; } first = false; fixptr = tail; eop = *tail = nasm_malloc(sizeof(extop)); tail = &eop->next; eop->next = NULL; eop->type = EOT_NOTHING; oper_num++; sign = +1; /* * is_comma_next() here is to distinguish this from * a string used as part of an expression... */ if (i == TOKEN_STR && is_comma_next()) { eop->type = EOT_DB_STRING; eop->stringval = tokval.t_charptr; eop->stringlen = tokval.t_inttwo; i = stdscan(NULL, &tokval); /* eat the comma */ } else if (i == TOKEN_STRFUNC) { bool parens = false; const char *funcname = tokval.t_charptr; enum strfunc func = tokval.t_integer; i = stdscan(NULL, &tokval); if (i == '(') { parens = true; i = stdscan(NULL, &tokval); } if (i != TOKEN_STR) { nasm_error(ERR_NONFATAL, "%s must be followed by a string constant", funcname); eop->type = EOT_NOTHING; } else { eop->type = EOT_DB_STRING_FREE; eop->stringlen = string_transform(tokval.t_charptr, tokval.t_inttwo, &eop->stringval, func); if (eop->stringlen == (size_t)-1) { nasm_error(ERR_NONFATAL, "invalid string for transform"); eop->type = EOT_NOTHING; } } if (parens && i && i != ')') { i = stdscan(NULL, &tokval); if (i != ')') { nasm_error(ERR_NONFATAL, "unterminated %s function", funcname); } } if (i && i != ',') i = stdscan(NULL, &tokval); } else if (i == '-' || i == '+') { char *save = stdscan_get(); int token = i; sign = (i == '-') ? -1 : 1; i = stdscan(NULL, &tokval); if (i != TOKEN_FLOAT) { stdscan_set(save); i = tokval.t_type = token; goto is_expression; } else { goto is_float; } } else if (i == TOKEN_FLOAT) { is_float: eop->type = EOT_DB_STRING; result->eops_float = true; eop->stringlen = idata_bytes(result->opcode); if (eop->stringlen > 16) { nasm_error(ERR_NONFATAL, "floating-point constant" " encountered in DY instruction"); eop->stringlen = 0; } else if (eop->stringlen < 1) { nasm_error(ERR_NONFATAL, "floating-point constant" " encountered in unknown instruction"); /* * fix suggested by Pedro Gimeno... original line was: * eop->type = EOT_NOTHING; */ eop->stringlen = 0; } eop = nasm_realloc(eop, sizeof(extop) + eop->stringlen); tail = &eop->next; *fixptr = eop; eop->stringval = (char *)eop + sizeof(extop); if (!eop->stringlen || !float_const(tokval.t_charptr, sign, (uint8_t *)eop->stringval, eop->stringlen, nasm_error)) eop->type = EOT_NOTHING; i = stdscan(NULL, &tokval); /* eat the comma */ } else { /* anything else, assume it is an expression */ expr *value; is_expression: value = evaluate(stdscan, NULL, &tokval, NULL, critical, nasm_error, NULL); i = tokval.t_type; if (!value) { /* error in evaluator */ result->opcode = I_none; /* unrecoverable parse error: */ return result; /* ignore this instruction */ } if (is_unknown(value)) { eop->type = EOT_DB_NUMBER; eop->offset = 0; /* doesn't matter what we put */ eop->segment = eop->wrt = NO_SEG; /* likewise */ } else if (is_reloc(value)) { eop->type = EOT_DB_NUMBER; eop->offset = reloc_value(value); eop->segment = reloc_seg(value); eop->wrt = reloc_wrt(value); } else { nasm_error(ERR_NONFATAL, "operand %d: expression is not simple" " or relocatable", oper_num); } } /* * We're about to call stdscan(), which will eat the * comma that we're currently sitting on between * arguments. However, we'd better check first that it * _is_ a comma. */ if (i == TOKEN_EOS) /* also could be EOL */ break; if (i != ',') { nasm_error(ERR_NONFATAL, "comma expected after operand %d", oper_num); result->opcode = I_none;/* unrecoverable parse error: */ return result; /* ignore this instruction */ } } if (result->opcode == I_INCBIN) { /* * Correct syntax for INCBIN is that there should be * one string operand, followed by one or two numeric * operands. */ if (!result->eops || result->eops->type != EOT_DB_STRING) nasm_error(ERR_NONFATAL, "`incbin' expects a file name"); else if (result->eops->next && result->eops->next->type != EOT_DB_NUMBER) nasm_error(ERR_NONFATAL, "`incbin': second parameter is" " non-numeric"); else if (result->eops->next && result->eops->next->next && result->eops->next->next->type != EOT_DB_NUMBER) nasm_error(ERR_NONFATAL, "`incbin': third parameter is" " non-numeric"); else if (result->eops->next && result->eops->next->next && result->eops->next->next->next) nasm_error(ERR_NONFATAL, "`incbin': more than three parameters"); else return result; /* * If we reach here, one of the above errors happened. * Throw the instruction away. */ result->opcode = I_none; return result; } else /* DB ... */ if (oper_num == 0) nasm_error(ERR_WARNING | ERR_PASS1, "no operand for data declaration"); else result->operands = oper_num; return result; } /* * Now we begin to parse the operands. There may be up to four * of these, separated by commas, and terminated by a zero token. */ for (operand = 0; operand < MAX_OPERANDS; operand++) { expr *value; /* used most of the time */ int mref; /* is this going to be a memory ref? */ int bracket; /* is it a [] mref, or a & mref? */ int setsize = 0; result->oprs[operand].disp_size = 0; /* have to zero this whatever */ result->oprs[operand].eaflags = 0; /* and this */ result->oprs[operand].opflags = 0; i = stdscan(NULL, &tokval); if (i == TOKEN_EOS) break; /* end of operands: get out of here */ else if (first && i == ':') { insn_is_label = true; goto restart_parse; } first = false; result->oprs[operand].type = 0; /* so far, no override */ while (i == TOKEN_SPECIAL) { /* size specifiers */ switch ((int)tokval.t_integer) { case S_BYTE: if (!setsize) /* we want to use only the first */ result->oprs[operand].type |= BITS8; setsize = 1; break; case S_WORD: if (!setsize) result->oprs[operand].type |= BITS16; setsize = 1; break; case S_DWORD: case S_LONG: if (!setsize) result->oprs[operand].type |= BITS32; setsize = 1; break; case S_QWORD: if (!setsize) result->oprs[operand].type |= BITS64; setsize = 1; break; case S_TWORD: if (!setsize) result->oprs[operand].type |= BITS80; setsize = 1; break; case S_OWORD: if (!setsize) result->oprs[operand].type |= BITS128; setsize = 1; break; case S_YWORD: if (!setsize) result->oprs[operand].type |= BITS256; setsize = 1; break; case S_TO: result->oprs[operand].type |= TO; break; case S_STRICT: result->oprs[operand].type |= STRICT; break; case S_FAR: result->oprs[operand].type |= FAR; break; case S_NEAR: result->oprs[operand].type |= NEAR; break; case S_SHORT: result->oprs[operand].type |= SHORT; break; default: nasm_error(ERR_NONFATAL, "invalid operand size specification"); } i = stdscan(NULL, &tokval); } if (i == '[' || i == '&') { /* memory reference */ mref = true; bracket = (i == '['); i = stdscan(NULL, &tokval); /* then skip the colon */ while (i == TOKEN_SPECIAL || i == TOKEN_PREFIX) { process_size_override(result, operand); i = stdscan(NULL, &tokval); } } else { /* immediate operand, or register */ mref = false; bracket = false; /* placate optimisers */ } if ((result->oprs[operand].type & FAR) && !mref && result->opcode != I_JMP && result->opcode != I_CALL) { nasm_error(ERR_NONFATAL, "invalid use of FAR operand specifier"); } value = evaluate(stdscan, NULL, &tokval, &result->oprs[operand].opflags, critical, nasm_error, &hints); i = tokval.t_type; if (result->oprs[operand].opflags & OPFLAG_FORWARD) { result->forw_ref = true; } if (!value) { /* nasm_error in evaluator */ result->opcode = I_none; /* unrecoverable parse error: */ return result; /* ignore this instruction */ } if (i == ':' && mref) { /* it was seg:offset */ /* * Process the segment override. */ if (value[1].type != 0 || value->value != 1 || !IS_SREG(value->type)) nasm_error(ERR_NONFATAL, "invalid segment override"); else if (result->prefixes[PPS_SEG]) nasm_error(ERR_NONFATAL, "instruction has conflicting segment overrides"); else { result->prefixes[PPS_SEG] = value->type; if (IS_FSGS(value->type)) result->oprs[operand].eaflags |= EAF_FSGS; } i = stdscan(NULL, &tokval); /* then skip the colon */ while (i == TOKEN_SPECIAL || i == TOKEN_PREFIX) { process_size_override(result, operand); i = stdscan(NULL, &tokval); } value = evaluate(stdscan, NULL, &tokval, &result->oprs[operand].opflags, critical, nasm_error, &hints); i = tokval.t_type; if (result->oprs[operand].opflags & OPFLAG_FORWARD) { result->forw_ref = true; } /* and get the offset */ if (!value) { /* but, error in evaluator */ result->opcode = I_none; /* unrecoverable parse error: */ return result; /* ignore this instruction */ } } recover = false; if (mref && bracket) { /* find ] at the end */ if (i != ']') { nasm_error(ERR_NONFATAL, "parser: expecting ]"); recover = true; } else { /* we got the required ] */ i = stdscan(NULL, &tokval); if (i != 0 && i != ',') { nasm_error(ERR_NONFATAL, "comma or end of line expected"); recover = true; } } } else { /* immediate operand */ if (i != 0 && i != ',' && i != ':') { nasm_error(ERR_NONFATAL, "comma, colon or end of line expected"); recover = true; } else if (i == ':') { result->oprs[operand].type |= COLON; } } if (recover) { do { /* error recovery */ i = stdscan(NULL, &tokval); } while (i != 0 && i != ','); } /* * now convert the exprs returned from evaluate() * into operand descriptions... */ if (mref) { /* it's a memory reference */ expr *e = value; int b, i, s; /* basereg, indexreg, scale */ int64_t o; /* offset */ b = i = -1, o = s = 0; result->oprs[operand].hintbase = hints.base; result->oprs[operand].hinttype = hints.type; if (e->type && e->type <= EXPR_REG_END) { /* this bit's a register */ if (e->value == 1) /* in fact it can be basereg */ b = e->type; else /* no, it has to be indexreg */ i = e->type, s = e->value; e++; } if (e->type && e->type <= EXPR_REG_END) { /* it's a 2nd register */ if (b != -1) /* If the first was the base, ... */ i = e->type, s = e->value; /* second has to be indexreg */ else if (e->value != 1) { /* If both want to be index */ nasm_error(ERR_NONFATAL, "beroset-p-592-invalid effective address"); result->opcode = I_none; return result; } else b = e->type; e++; } if (e->type != 0) { /* is there an offset? */ if (e->type <= EXPR_REG_END) { /* in fact, is there an error? */ nasm_error(ERR_NONFATAL, "beroset-p-603-invalid effective address"); result->opcode = I_none; return result; } else { if (e->type == EXPR_UNKNOWN) { result->oprs[operand].opflags |= OPFLAG_UNKNOWN; o = 0; /* doesn't matter what */ result->oprs[operand].wrt = NO_SEG; /* nor this */ result->oprs[operand].segment = NO_SEG; /* or this */ while (e->type) e++; /* go to the end of the line */ } else { if (e->type == EXPR_SIMPLE) { o = e->value; e++; } if (e->type == EXPR_WRT) { result->oprs[operand].wrt = e->value; e++; } else result->oprs[operand].wrt = NO_SEG; /* * Look for a segment base type. */ if (e->type && e->type < EXPR_SEGBASE) { nasm_error(ERR_NONFATAL, "beroset-p-630-invalid effective address"); result->opcode = I_none; return result; } while (e->type && e->value == 0) e++; if (e->type && e->value != 1) { nasm_error(ERR_NONFATAL, "beroset-p-637-invalid effective address"); result->opcode = I_none; return result; } if (e->type) { result->oprs[operand].segment = e->type - EXPR_SEGBASE; e++; } else result->oprs[operand].segment = NO_SEG; while (e->type && e->value == 0) e++; if (e->type) { nasm_error(ERR_NONFATAL, "beroset-p-650-invalid effective address"); result->opcode = I_none; return result; } } } } else { o = 0; result->oprs[operand].wrt = NO_SEG; result->oprs[operand].segment = NO_SEG; } if (e->type != 0) { /* there'd better be nothing left! */ nasm_error(ERR_NONFATAL, "beroset-p-663-invalid effective address"); result->opcode = I_none; return result; } /* It is memory, but it can match any r/m operand */ result->oprs[operand].type |= MEMORY_ANY; if (b == -1 && (i == -1 || s == 0)) { int is_rel = globalbits == 64 && !(result->oprs[operand].eaflags & EAF_ABS) && ((globalrel && !(result->oprs[operand].eaflags & EAF_FSGS)) || (result->oprs[operand].eaflags & EAF_REL)); result->oprs[operand].type |= is_rel ? IP_REL : MEM_OFFS; } result->oprs[operand].basereg = b; result->oprs[operand].indexreg = i; result->oprs[operand].scale = s; result->oprs[operand].offset = o; } else { /* it's not a memory reference */ if (is_just_unknown(value)) { /* it's immediate but unknown */ result->oprs[operand].type |= IMMEDIATE; result->oprs[operand].opflags |= OPFLAG_UNKNOWN; result->oprs[operand].offset = 0; /* don't care */ result->oprs[operand].segment = NO_SEG; /* don't care again */ result->oprs[operand].wrt = NO_SEG; /* still don't care */ if(optimizing >= 0 && !(result->oprs[operand].type & STRICT)) { /* Be optimistic */ result->oprs[operand].type |= SBYTE16 | SBYTE32 | SBYTE64 | UDWORD64 | SDWORD64; } } else if (is_reloc(value)) { /* it's immediate */ result->oprs[operand].type |= IMMEDIATE; result->oprs[operand].offset = reloc_value(value); result->oprs[operand].segment = reloc_seg(value); result->oprs[operand].wrt = reloc_wrt(value); if (is_simple(value)) { if (reloc_value(value) == 1) result->oprs[operand].type |= UNITY; if (optimizing >= 0 && !(result->oprs[operand].type & STRICT)) { int64_t v64 = reloc_value(value); int32_t v32 = (int32_t)v64; int16_t v16 = (int16_t)v32; if (v64 >= -128 && v64 <= 127) result->oprs[operand].type |= SBYTE64; if (v32 >= -128 && v32 <= 127) result->oprs[operand].type |= SBYTE32; if (v16 >= -128 && v16 <= 127) result->oprs[operand].type |= SBYTE16; if ((uint64_t)v64 <= UINT64_C(0xffffffff)) result->oprs[operand].type |= UDWORD64; if (v64 >= -INT64_C(0x80000000) && v64 <= INT64_C(0x7fffffff)) result->oprs[operand].type |= SDWORD64; } } } else { /* it's a register */ unsigned int rs; if (value->type >= EXPR_SIMPLE || value->value != 1) { nasm_error(ERR_NONFATAL, "invalid operand type"); result->opcode = I_none; return result; } /* * check that its only 1 register, not an expression... */ for (i = 1; value[i].type; i++) if (value[i].value) { nasm_error(ERR_NONFATAL, "invalid operand type"); result->opcode = I_none; return result; } /* clear overrides, except TO which applies to FPU regs */ if (result->oprs[operand].type & ~TO) { /* * we want to produce a warning iff the specified size * is different from the register size */ rs = result->oprs[operand].type & SIZE_MASK; } else rs = 0; result->oprs[operand].type &= TO; result->oprs[operand].type |= REGISTER; result->oprs[operand].type |= nasm_reg_flags[value->type]; result->oprs[operand].basereg = value->type; if (rs && (result->oprs[operand].type & SIZE_MASK) != rs) nasm_error(ERR_WARNING | ERR_PASS1, "register size specification ignored"); } } } result->operands = operand; /* set operand count */ /* clear remaining operands */ while (operand < MAX_OPERANDS) result->oprs[operand++].type = 0; /* * Transform RESW, RESD, RESQ, REST, RESO, RESY into RESB. */ switch (result->opcode) { case I_RESW: result->opcode = I_RESB; result->oprs[0].offset *= 2; break; case I_RESD: result->opcode = I_RESB; result->oprs[0].offset *= 4; break; case I_RESQ: result->opcode = I_RESB; result->oprs[0].offset *= 8; break; case I_REST: result->opcode = I_RESB; result->oprs[0].offset *= 10; break; case I_RESO: result->opcode = I_RESB; result->oprs[0].offset *= 16; break; case I_RESY: result->opcode = I_RESB; result->oprs[0].offset *= 32; break; default: break; } return result; }
static void process_size_override(insn *result, int operand) { if (tasm_compatible_mode) { switch ((int)tokval.t_integer) { /* For TASM compatibility a size override inside the * brackets changes the size of the operand, not the * address type of the operand as it does in standard * NASM syntax. Hence: * * mov eax,[DWORD val] * * is valid syntax in TASM compatibility mode. Note that * you lose the ability to override the default address * type for the instruction, but we never use anything * but 32-bit flat model addressing in our code. */ case S_BYTE: result->oprs[operand].type |= BITS8; break; case S_WORD: result->oprs[operand].type |= BITS16; break; case S_DWORD: case S_LONG: result->oprs[operand].type |= BITS32; break; case S_QWORD: result->oprs[operand].type |= BITS64; break; case S_TWORD: result->oprs[operand].type |= BITS80; break; case S_OWORD: result->oprs[operand].type |= BITS128; break; default: nasm_error(ERR_NONFATAL, "invalid operand size specification"); break; } } else { /* Standard NASM compatible syntax */ switch ((int)tokval.t_integer) { case S_NOSPLIT: result->oprs[operand].eaflags |= EAF_TIMESTWO; break; case S_REL: result->oprs[operand].eaflags |= EAF_REL; break; case S_ABS: result->oprs[operand].eaflags |= EAF_ABS; break; case S_BYTE: result->oprs[operand].disp_size = 8; result->oprs[operand].eaflags |= EAF_BYTEOFFS; break; case P_A16: case P_A32: case P_A64: if (result->prefixes[PPS_ASIZE] && result->prefixes[PPS_ASIZE] != tokval.t_integer) nasm_error(ERR_NONFATAL, "conflicting address size specifications"); else result->prefixes[PPS_ASIZE] = tokval.t_integer; break; case S_WORD: result->oprs[operand].disp_size = 16; result->oprs[operand].eaflags |= EAF_WORDOFFS; break; case S_DWORD: case S_LONG: result->oprs[operand].disp_size = 32; result->oprs[operand].eaflags |= EAF_WORDOFFS; break; case S_QWORD: result->oprs[operand].disp_size = 64; result->oprs[operand].eaflags |= EAF_WORDOFFS; break; default: nasm_error(ERR_NONFATAL, "invalid size specification in" " effective address"); break; } } }
void define_label(char *label, int32_t segment, int64_t offset, char *special, bool is_norm, bool isextrn) { union label *lptr; int exi; #ifdef DEBUG #if DEBUG<3 if (!strncmp(label, "debugdump", 9)) #endif nasm_error(ERR_DEBUG, "define_label (%s, %"PRIx32", %"PRIx64", %s, %d, %d)", label, segment, offset, special, is_norm, isextrn); #endif lptr = find_label(label, 1); if (!lptr) return; if (lptr->defn.is_global & DEFINED_BIT) { nasm_error(ERR_NONFATAL, "symbol `%s' redefined", label); return; } lptr->defn.is_global |= DEFINED_BIT; if (isextrn) lptr->defn.is_global |= EXTERN_BIT; if (!islocalchar(label[0]) && is_norm) { /* not local, but not special either */ prevlabel = lptr->defn.label; } else if (islocal(label) && !*prevlabel) { nasm_error(ERR_NONFATAL, "attempt to define a local label before any" " non-local labels"); } lptr->defn.segment = segment; lptr->defn.offset = offset; lptr->defn.is_norm = (!islocalchar(label[0]) && is_norm); if (pass0 == 1 || (!is_norm && !isextrn && (segment > 0) && (segment & 1))) { exi = !!(lptr->defn.is_global & GLOBAL_BIT); if (exi) { char *xsymbol; int slen; slen = strlen(lprefix); slen += strlen(lptr->defn.label); slen += strlen(lpostfix); slen++; /* room for that null char */ xsymbol = nasm_malloc(slen); snprintf(xsymbol, slen, "%s%s%s", lprefix, lptr->defn.label, lpostfix); ofmt->symdef(xsymbol, segment, offset, exi, special ? special : lptr->defn.special); ofmt->current_dfmt->debug_deflabel(xsymbol, segment, offset, exi, special ? special : lptr->defn.special); /** nasm_free(xsymbol); ! outobj.c stores the pointer; ouch!!! **/ } else { if ((lptr->defn.is_global & (GLOBAL_BIT | EXTERN_BIT)) != EXTERN_BIT) { ofmt->symdef(lptr->defn.label, segment, offset, exi, special ? special : lptr->defn.special); ofmt->current_dfmt->debug_deflabel(label, segment, offset, exi, special ? special : lptr->defn.special); } } } /* if (pass0 == 1) */ }
/* * segment registry */ static int32_t ieee_segment(char *name, int pass, int *bits) { /* * We call the label manager here to define a name for the new * segment, and when our _own_ label-definition stub gets * called in return, it should register the new segment name * using the pointer it gets passed. That way we save memory, * by sponging off the label manager. */ if (!name) { *bits = 16; if (!any_segs) return 0; return seghead->index; } else { struct ieeeSection *seg; int ieee_idx, attrs; bool rn_error; char *p; /* * Look for segment attributes. */ attrs = 0; while (*name == '.') name++; /* hack, but a documented one */ p = name; while (*p && !nasm_isspace(*p)) p++; if (*p) { *p++ = '\0'; while (*p && nasm_isspace(*p)) *p++ = '\0'; } while (*p) { while (*p && !nasm_isspace(*p)) p++; if (*p) { *p++ = '\0'; while (*p && nasm_isspace(*p)) *p++ = '\0'; } attrs++; } ieee_idx = 1; for (seg = seghead; seg; seg = seg->next) { ieee_idx++; if (!strcmp(seg->name, name)) { if (attrs > 0 && pass == 1) nasm_error(ERR_WARNING, "segment attributes specified on" " redeclaration of segment: ignoring"); if (seg->use32) *bits = 32; else *bits = 16; return seg->index; } } *segtail = seg = nasm_malloc(sizeof(*seg)); seg->next = NULL; segtail = &seg->next; seg->index = seg_alloc(); seg->ieee_index = ieee_idx; any_segs = true; seg->name = NULL; seg->currentpos = 0; seg->align = 1; /* default */ seg->use32 = *bits == 32; /* default to user spec */ seg->combine = CMB_PUBLIC; /* default */ seg->pubhead = NULL; seg->pubtail = &seg->pubhead; seg->data = NULL; seg->fptr = NULL; seg->lochead = NULL; seg->loctail = &seg->lochead; /* * Process the segment attributes. */ p = name; while (attrs--) { p += strlen(p); while (!*p) p++; /* * `p' contains a segment attribute. */ if (!nasm_stricmp(p, "private")) seg->combine = CMB_PRIVATE; else if (!nasm_stricmp(p, "public")) seg->combine = CMB_PUBLIC; else if (!nasm_stricmp(p, "common")) seg->combine = CMB_COMMON; else if (!nasm_stricmp(p, "use16")) seg->use32 = false; else if (!nasm_stricmp(p, "use32")) seg->use32 = true; else if (!nasm_strnicmp(p, "align=", 6)) { seg->align = readnum(p + 6, &rn_error); if (seg->align == 0) seg->align = 1; if (rn_error) { seg->align = 1; nasm_error(ERR_NONFATAL, "segment alignment should be" " numeric"); } switch ((int)seg->align) { case 1: /* BYTE */ case 2: /* WORD */ case 4: /* DWORD */ case 16: /* PARA */ case 256: /* PAGE */ case 8: case 32: case 64: case 128: break; default: nasm_error(ERR_NONFATAL, "invalid alignment value %d", seg->align); seg->align = 1; break; } } else if (!nasm_strnicmp(p, "absolute=", 9)) { seg->align = SEG_ABS + readnum(p + 9, &rn_error); if (rn_error) nasm_error(ERR_NONFATAL, "argument to `absolute' segment" " attribute should be numeric"); } } ieee_seg_needs_update = seg; if (seg->align >= SEG_ABS) define_label(name, NO_SEG, seg->align - SEG_ABS, NULL, false, false); else define_label(name, seg->index + 1, 0L, NULL, false, false); ieee_seg_needs_update = NULL; if (seg->use32) *bits = 32; else *bits = 16; return seg->index; } }
static void ieee_write_file(int debuginfo) { struct tm *thetime; time_t reltime; struct FileName *fn; struct ieeeSection *seg; struct ieeePublic *pub, *loc; struct ieeeExternal *ext; struct ieeeObjData *data; struct ieeeFixupp *fix; struct Array *arr; int i; /* * Write the module header */ ieee_putascii("MBFNASM,%02X%s.\n", strlen(ieee_infile), ieee_infile); /* * Write the NASM boast comment. */ ieee_putascii("CO0,%02X%s.\n", strlen(nasm_comment), nasm_comment); /* * write processor-specific information */ ieee_putascii("AD8,4,L.\n"); /* * date and time */ time(&reltime); thetime = localtime(&reltime); ieee_putascii("DT%04d%02d%02d%02d%02d%02d.\n", 1900 + thetime->tm_year, thetime->tm_mon + 1, thetime->tm_mday, thetime->tm_hour, thetime->tm_min, thetime->tm_sec); /* * if debugging, dump file names */ for (fn = fnhead; fn && debuginfo; fn = fn->next) { ieee_putascii("C0105,%02X%s.\n", strlen(fn->name), fn->name); } ieee_putascii("CO101,07ENDHEAD.\n"); /* * the standard doesn't specify when to put checksums, * we'll just do it periodically. */ ieee_putcs(false); /* * Write the section headers */ seg = seghead; if (!debuginfo && !strcmp(seg->name, "??LINE")) seg = seg->next; while (seg) { char buf[256]; char attrib; switch (seg->combine) { case CMB_PUBLIC: default: attrib = 'C'; break; case CMB_PRIVATE: attrib = 'S'; break; case CMB_COMMON: attrib = 'M'; break; } ieee_unqualified_name(buf, seg->name); if (seg->align >= SEG_ABS) { ieee_putascii("ST%X,A,%02X%s.\n", seg->ieee_index, strlen(buf), buf); ieee_putascii("ASL%X,%lX.\n", seg->ieee_index, (seg->align - SEG_ABS) * 16); } else { ieee_putascii("ST%X,%c,%02X%s.\n", seg->ieee_index, attrib, strlen(buf), buf); ieee_putascii("SA%X,%lX.\n", seg->ieee_index, seg->align); ieee_putascii("ASS%X,%X.\n", seg->ieee_index, seg->currentpos); } seg = seg->next; } /* * write the start address if there is one */ if (ieee_entry_seg) { for (seg = seghead; seg; seg = seg->next) if (seg->index == ieee_entry_seg) break; if (!seg) nasm_error(ERR_PANIC, "Start address records are incorrect"); else ieee_putascii("ASG,R%X,%lX,+.\n", seg->ieee_index, ieee_entry_ofs); } ieee_putcs(false); /* * Write the publics */ i = 1; for (seg = seghead; seg; seg = seg->next) { for (pub = seg->pubhead; pub; pub = pub->next) { char buf[256]; ieee_unqualified_name(buf, pub->name); ieee_putascii("NI%X,%02X%s.\n", i, strlen(buf), buf); if (pub->segment == -1) ieee_putascii("ASI%X,R%X,%lX,+.\n", i, pub->index, pub->offset); else ieee_putascii("ASI%X,%lX,%lX,+.\n", i, pub->segment * 16, pub->offset); if (debuginfo) { if (pub->type >= 0x100) ieee_putascii("ATI%X,T%X.\n", i, pub->type - 0x100); else ieee_putascii("ATI%X,%X.\n", i, pub->type); } i++; } } pub = fpubhead; i = 1; while (pub) { char buf[256]; ieee_unqualified_name(buf, pub->name); ieee_putascii("NI%X,%02X%s.\n", i, strlen(buf), buf); if (pub->segment == -1) ieee_putascii("ASI%X,R%X,%lX,+.\n", i, pub->index, pub->offset); else ieee_putascii("ASI%X,%lX,%lX,+.\n", i, pub->segment * 16, pub->offset); if (debuginfo) { if (pub->type >= 0x100) ieee_putascii("ATI%X,T%X.\n", i, pub->type - 0x100); else ieee_putascii("ATI%X,%X.\n", i, pub->type); } i++; pub = pub->next; } /* * Write the externals */ ext = exthead; i = 1; while (ext) { char buf[256]; ieee_unqualified_name(buf, ext->name); ieee_putascii("NX%X,%02X%s.\n", i++, strlen(buf), buf); ext = ext->next; } ieee_putcs(false); /* * IEEE doesn't have a standard pass break record * so use the ladsoft variant */ ieee_putascii("CO100,06ENDSYM.\n"); /* * now put types */ i = ARRAY_BOT; for (arr = arrhead; arr && debuginfo; arr = arr->next) { ieee_putascii("TY%X,20,%X,%lX.\n", i++, arr->basetype, arr->size); } /* * now put locals */ i = 1; for (seg = seghead; seg && debuginfo; seg = seg->next) { for (loc = seg->lochead; loc; loc = loc->next) { char buf[256]; ieee_unqualified_name(buf, loc->name); ieee_putascii("NN%X,%02X%s.\n", i, strlen(buf), buf); if (loc->segment == -1) ieee_putascii("ASN%X,R%X,%lX,+.\n", i, loc->index, loc->offset); else ieee_putascii("ASN%X,%lX,%lX,+.\n", i, loc->segment * 16, loc->offset); if (debuginfo) { if (loc->type >= 0x100) ieee_putascii("ATN%X,T%X.\n", i, loc->type - 0x100); else ieee_putascii("ATN%X,%X.\n", i, loc->type); } i++; } } /* * put out section data; */ seg = seghead; if (!debuginfo && !strcmp(seg->name, "??LINE")) seg = seg->next; while (seg) { if (seg->currentpos) { int32_t size, org = 0; data = seg->data; ieee_putascii("SB%X.\n", seg->ieee_index); fix = seg->fptr; while (fix) { size = HUNKSIZE - (org % HUNKSIZE); size = size + org > seg->currentpos ? seg->currentpos - org : size; size = fix->offset - org > size ? size : fix->offset - org; org = ieee_putld(org, org + size, data->data); if (org % HUNKSIZE == 0) data = data->next; if (org == fix->offset) { org += ieee_putlr(fix); fix = fix->next; } } while (org < seg->currentpos && data) { size = seg->currentpos - org > HUNKSIZE ? HUNKSIZE : seg->currentpos - org; org = ieee_putld(org, org + size, data->data); data = data->next; } ieee_putcs(false); } seg = seg->next; } /* * module end record */ ieee_putascii("ME.\n"); }
/* * The "normal" argument decides if we should update the local segment * base name or not. */ void define_label(const char *label, int32_t segment, int64_t offset, bool normal) { union label *lptr; bool created, changed; int64_t size; int64_t lastdef; /* * The backend may invoke this before pass 1, so treat that as * a special "pass". */ const int64_t lpass = passn + 1; /* * Phase errors here can be one of two types: a new label appears, * or the offset changes. Increment global_offset_changed when that * happens, to tell the assembler core to make another pass. */ lptr = find_label(label, true, &created); lastdef = lptr->defn.defined; if (segment) { /* We are actually defining this label */ if (lptr->defn.type == LBL_EXTERN) { /* auto-promote EXTERN to GLOBAL */ lptr->defn.type = LBL_GLOBAL; lastdef = 0; /* We are "re-creating" this label */ } } else { /* It's a pseudo-segment (extern, common) */ segment = lptr->defn.segment ? lptr->defn.segment : seg_alloc(); } if (lastdef || lptr->defn.type == LBL_BACKEND) { /* * We have seen this on at least one previous pass, or * potentially earlier in this same pass (in which case we * will probably error out further down.) */ mangle_label_name(lptr); handle_herelabel(lptr, &segment, &offset); } if (ismagic(label) && lptr->defn.type == LBL_LOCAL) lptr->defn.type = LBL_SPECIAL; if (set_prevlabel(label) && normal) prevlabel = lptr->defn.label; if (lptr->defn.type == LBL_COMMON) { size = offset; offset = 0; } else { size = 0; /* This is a hack... */ } changed = created || !lastdef || lptr->defn.segment != segment || lptr->defn.offset != offset || lptr->defn.size != size; global_offset_changed += changed; if (lastdef == lpass) { int32_t saved_line = 0; const char *saved_fname = NULL; int noteflags; /* * Defined elsewhere in the program, seen in this pass. */ if (changed) { nasm_error(ERR_NONFATAL, "label `%s' inconsistently redefined", lptr->defn.label); noteflags = ERR_NOTE|ERR_HERE; } else { nasm_error(ERR_WARNING|WARN_LABEL_REDEF|ERR_PASS2, "label `%s' redefined to an identical value", lptr->defn.label); noteflags = ERR_NOTE|ERR_HERE|WARN_LABEL_REDEF|ERR_PASS2; } src_get(&saved_line, &saved_fname); src_set(lptr->defn.def_line, lptr->defn.def_file); nasm_error(noteflags, "label `%s' originally defined", lptr->defn.label); src_set(saved_line, saved_fname); } else if (changed && pass0 > 1 && lptr->defn.type != LBL_SPECIAL) { /* * WARN_LABEL_LATE defaults to an error, as this should never * actually happen. Just in case this is a backwards * compatibility problem, still make it a warning so that the * user can suppress or demote it. * * As a special case, LBL_SPECIAL symbols are allowed to be changed * even during the last pass. */ nasm_error(ERR_WARNING|WARN_LABEL_LATE, "label `%s' %s during code generation", lptr->defn.label, created ? "defined" : "changed"); } lptr->defn.segment = segment; lptr->defn.offset = offset; lptr->defn.size = size; lptr->defn.defined = lpass; if (changed || lastdef != lpass) src_get(&lptr->defn.def_line, &lptr->defn.def_file); if (lastdef != lpass) out_symdef(lptr); }