| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- /* ----------------------------------------------------------------------- *
- *
- * Copyright 1996-2018 The NASM Authors - All Rights Reserved
- * See the file AUTHORS included with the NASM distribution for
- * the specific copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following
- * conditions are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * 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 COPYRIGHT HOLDERS AND
- * 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 COPYRIGHT OWNER OR
- * 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.
- *
- * ----------------------------------------------------------------------- */
- /*
- * labels.c label handling for the Netwide Assembler
- */
- #include "compiler.h"
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include "nasm.h"
- #include "nasmlib.h"
- #include "error.h"
- #include "hashtbl.h"
- #include "labels.h"
- /*
- * A dot-local label is one that begins with exactly one period. Things
- * that begin with _two_ periods are NASM-specific things.
- *
- * If TASM compatibility is enabled, a local label can also begin with
- * @@.
- */
- static bool islocal(const char *l)
- {
- if (tasm_compatible_mode) {
- if (l[0] == '@' && l[1] == '@')
- return true;
- }
- return (l[0] == '.' && l[1] != '.');
- }
- /*
- * Return true if this falls into NASM's '..' namespace
- */
- static bool ismagic(const char *l)
- {
- return l[0] == '.' && l[1] == '.' && l[2] != '@';
- }
- /*
- * Return true if we should update the local label base
- * as a result of this symbol. We must exclude local labels
- * as well as any kind of special labels, including ..@ ones.
- */
- static bool set_prevlabel(const char *l)
- {
- if (tasm_compatible_mode) {
- if (l[0] == '@' && l[1] == '@')
- return false;
- }
- return l[0] != '.';
- }
- #define LABEL_BLOCK 128 /* no. of labels/block */
- #define LBLK_SIZE (LABEL_BLOCK * sizeof(union label))
- #define END_LIST -3 /* don't clash with NO_SEG! */
- #define END_BLOCK -2
- #define PERMTS_SIZE 16384 /* size of text blocks */
- #if (PERMTS_SIZE < IDLEN_MAX)
- #error "IPERMTS_SIZE must be greater than or equal to IDLEN_MAX"
- #endif
- /* string values for enum label_type */
- static const char * const types[] =
- {"local", "global", "static", "extern", "common", "special",
- "output format special"};
- union label { /* actual label structures */
- struct {
- int32_t segment;
- int32_t subsection; /* Available for ofmt->herelabel() */
- int64_t offset;
- int64_t size;
- int64_t defined; /* 0 if undefined, passn+1 for when defn seen */
- char *label, *mangled, *special;
- const char *def_file; /* Where defined */
- int32_t def_line;
- enum label_type type, mangled_type;
- } defn;
- struct {
- int32_t movingon;
- int64_t dummy;
- union label *next;
- } admin;
- };
- struct permts { /* permanent text storage */
- struct permts *next; /* for the linked list */
- unsigned int size, usage; /* size and used space in ... */
- char data[PERMTS_SIZE]; /* ... the data block itself */
- };
- #define PERMTS_HEADER offsetof(struct permts, data)
- uint64_t global_offset_changed; /* counter for global offset changes */
- static struct hash_table ltab; /* labels hash table */
- static union label *ldata; /* all label data blocks */
- static union label *lfree; /* labels free block */
- static struct permts *perm_head; /* start of perm. text storage */
- static struct permts *perm_tail; /* end of perm. text storage */
- static void init_block(union label *blk);
- static char *perm_alloc(size_t len);
- static char *perm_copy(const char *string);
- static char *perm_copy3(const char *s1, const char *s2, const char *s3);
- static const char *mangle_label_name(union label *lptr);
- static const char *prevlabel;
- static bool initialized = false;
- /*
- * Emit a symdef to the output and the debug format backends.
- */
- static void out_symdef(union label *lptr)
- {
- int backend_type;
- int64_t backend_offset;
- /* Backend-defined special segments are passed to symdef immediately */
- if (pass0 == 2) {
- /* Emit special fixups for globals and commons */
- switch (lptr->defn.type) {
- case LBL_GLOBAL:
- case LBL_EXTERN:
- case LBL_COMMON:
- if (lptr->defn.special)
- ofmt->symdef(lptr->defn.mangled, 0, 0, 3, lptr->defn.special);
- break;
- default:
- break;
- }
- return;
- }
- if (pass0 != 1 && lptr->defn.type != LBL_BACKEND)
- return;
- /* Clean up this hack... */
- switch(lptr->defn.type) {
- case LBL_GLOBAL:
- case LBL_EXTERN:
- backend_type = 1;
- backend_offset = lptr->defn.offset;
- break;
- case LBL_COMMON:
- backend_type = 2;
- backend_offset = lptr->defn.size;
- break;
- default:
- backend_type = 0;
- backend_offset = lptr->defn.offset;
- break;
- }
- /* Might be necessary for a backend symbol */
- mangle_label_name(lptr);
- ofmt->symdef(lptr->defn.mangled, lptr->defn.segment,
- backend_offset, backend_type,
- lptr->defn.special);
- /*
- * NASM special symbols are not passed to the debug format; none
- * of the current backends want to see them.
- */
- if (lptr->defn.type == LBL_SPECIAL || lptr->defn.type == LBL_BACKEND)
- return;
- dfmt->debug_deflabel(lptr->defn.mangled, lptr->defn.segment,
- lptr->defn.offset, backend_type,
- lptr->defn.special);
- }
- /*
- * 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(const char *label, bool create, bool *created)
- {
- union label *lptr, **lpp;
- char *label_str = NULL;
- struct hash_insert ip;
- nasm_assert(label != NULL);
- if (islocal(label))
- label = label_str = nasm_strcat(prevlabel, label);
- lpp = (union label **) hash_find(<ab, label, &ip);
- lptr = lpp ? *lpp : NULL;
- if (lptr || !create) {
- if (created)
- *created = false;
- return lptr;
- }
- /* Create a new label... */
- if (lfree->admin.movingon == END_BLOCK) {
- /*
- * must allocate a new block
- */
- lfree->admin.next = nasm_malloc(LBLK_SIZE);
- lfree = lfree->admin.next;
- init_block(lfree);
- }
- if (created)
- *created = true;
- nasm_zero(*lfree);
- lfree->defn.label = perm_copy(label);
- lfree->defn.subsection = NO_SEG;
- if (label_str)
- nasm_free(label_str);
- hash_add(&ip, lfree->defn.label, lfree);
- return lfree++;
- }
- bool lookup_label(const char *label, int32_t *segment, int64_t *offset)
- {
- union label *lptr;
- if (!initialized)
- return false;
- lptr = find_label(label, false, NULL);
- if (lptr && lptr->defn.defined) {
- *segment = lptr->defn.segment;
- *offset = lptr->defn.offset;
- return true;
- }
- return false;
- }
- bool is_extern(const char *label)
- {
- union label *lptr;
- if (!initialized)
- return false;
- lptr = find_label(label, false, NULL);
- return lptr && lptr->defn.type == LBL_EXTERN;
- }
- static const char *mangle_strings[] = {"", "", "", ""};
- static bool mangle_string_set[ARRAY_SIZE(mangle_strings)];
- /*
- * Set a prefix or suffix
- */
- void set_label_mangle(enum mangle_index which, const char *what)
- {
- if (mangle_string_set[which])
- return; /* Once set, do not change */
- mangle_strings[which] = perm_copy(what);
- mangle_string_set[which] = true;
- }
- /*
- * Format a label name with appropriate prefixes and suffixes
- */
- static const char *mangle_label_name(union label *lptr)
- {
- const char *prefix;
- const char *suffix;
- if (likely(lptr->defn.mangled &&
- lptr->defn.mangled_type == lptr->defn.type))
- return lptr->defn.mangled; /* Already mangled */
- switch (lptr->defn.type) {
- case LBL_GLOBAL:
- case LBL_STATIC:
- case LBL_EXTERN:
- prefix = mangle_strings[LM_GPREFIX];
- suffix = mangle_strings[LM_GSUFFIX];
- break;
- case LBL_BACKEND:
- case LBL_SPECIAL:
- prefix = suffix = "";
- break;
- default:
- prefix = mangle_strings[LM_LPREFIX];
- suffix = mangle_strings[LM_LSUFFIX];
- break;
- }
- lptr->defn.mangled_type = lptr->defn.type;
- if (!(*prefix) && !(*suffix))
- lptr->defn.mangled = lptr->defn.label;
- else
- lptr->defn.mangled = perm_copy3(prefix, lptr->defn.label, suffix);
- return lptr->defn.mangled;
- }
- static void
- handle_herelabel(union label *lptr, int32_t *segment, int64_t *offset)
- {
- int32_t oldseg;
- if (likely(!ofmt->herelabel))
- return;
- if (unlikely(location.segment == NO_SEG))
- return;
- oldseg = *segment;
- if (oldseg == location.segment && *offset == location.offset) {
- /* This label is defined at this location */
- int32_t newseg;
- bool copyoffset = false;
- nasm_assert(lptr->defn.mangled);
- newseg = ofmt->herelabel(lptr->defn.mangled, lptr->defn.type,
- oldseg, &lptr->defn.subsection, ©offset);
- if (likely(newseg == oldseg))
- return;
- *segment = newseg;
- if (copyoffset) {
- /* Maintain the offset from the old to the new segment */
- switch_segment(newseg);
- location.offset = *offset;
- } else {
- /* Keep a separate offset for the new segment */
- *offset = switch_segment(newseg);
- }
- }
- }
- 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;
- }
- bool declare_label(const char *label, enum label_type type, const char *special)
- {
- union label *lptr = find_label(label, true, NULL);
- return declare_label_lptr(lptr, type, special);
- }
- /*
- * 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);
- }
- /*
- * Define a special backend label
- */
- void backend_label(const char *label, int32_t segment, int64_t offset)
- {
- if (!declare_label(label, LBL_BACKEND, NULL))
- return;
- define_label(label, segment, offset, false);
- }
- int init_labels(void)
- {
- hash_init(<ab, HASH_LARGE);
- ldata = lfree = nasm_malloc(LBLK_SIZE);
- init_block(lfree);
- perm_head = perm_tail =
- nasm_malloc(sizeof(struct permts));
- perm_head->next = NULL;
- perm_head->size = PERMTS_SIZE;
- perm_head->usage = 0;
- prevlabel = "";
- initialized = true;
- return 0;
- }
- void cleanup_labels(void)
- {
- union label *lptr, *lhold;
- initialized = false;
- hash_free(<ab);
- lptr = lhold = ldata;
- while (lptr) {
- lptr = &lptr[LABEL_BLOCK-1];
- lptr = lptr->admin.next;
- nasm_free(lhold);
- lhold = lptr;
- }
- while (perm_head) {
- perm_tail = perm_head;
- perm_head = perm_head->next;
- nasm_free(perm_tail);
- }
- }
- static void init_block(union label *blk)
- {
- int j;
- for (j = 0; j < LABEL_BLOCK - 1; j++)
- blk[j].admin.movingon = END_LIST;
- blk[LABEL_BLOCK - 1].admin.movingon = END_BLOCK;
- blk[LABEL_BLOCK - 1].admin.next = NULL;
- }
- static char * safe_alloc perm_alloc(size_t len)
- {
- char *p;
- if (perm_tail->size - perm_tail->usage < len) {
- size_t alloc_len = (len > PERMTS_SIZE) ? len : PERMTS_SIZE;
- perm_tail->next = nasm_malloc(PERMTS_HEADER + alloc_len);
- perm_tail = perm_tail->next;
- perm_tail->next = NULL;
- perm_tail->size = alloc_len;
- perm_tail->usage = 0;
- }
- p = perm_tail->data + perm_tail->usage;
- perm_tail->usage += len;
- return p;
- }
- static char *perm_copy(const char *string)
- {
- char *p;
- size_t len;
- if (!string)
- return NULL;
- len = strlen(string)+1; /* Include final NUL */
- p = perm_alloc(len);
- memcpy(p, string, len);
- return p;
- }
- static char *
- perm_copy3(const char *s1, const char *s2, const char *s3)
- {
- char *p;
- size_t l1 = strlen(s1);
- size_t l2 = strlen(s2);
- size_t l3 = strlen(s3)+1; /* Include final NUL */
- p = perm_alloc(l1+l2+l3);
- memcpy(p, s1, l1);
- memcpy(p+l1, s2, l2);
- memcpy(p+l1+l2, s3, l3);
- return p;
- }
- const char *local_scope(const char *label)
- {
- return islocal(label) ? prevlabel : "";
- }
- /*
- * Notes regarding bug involving redefinition of external segments.
- *
- * Up to and including v0.97, the following code didn't work. From 0.97
- * developers release 2 onwards, it will generate an error.
- *
- * EXTERN extlabel
- * newlabel EQU extlabel + 1
- *
- * The results of allowing this code through are that two import records
- * are generated, one for 'extlabel' and one for 'newlabel'.
- *
- * The reason for this is an inadequacy in the defined interface between
- * the label manager and the output formats. The problem lies in how the
- * output format driver tells that a label is an external label for which
- * a label import record must be produced. Most (all except bin?) produce
- * the record if the segment number of the label is not one of the internal
- * segments that the output driver is producing.
- *
- * A simple fix to this would be to make the output formats keep track of
- * which symbols they've produced import records for, and make them not
- * produce import records for segments that are already defined.
- *
- * The best way, which is slightly harder but reduces duplication of code
- * and should therefore make the entire system smaller and more stable is
- * to change the interface between assembler, define_label(), and
- * the output module. The changes that are needed are:
- *
- * The semantics of the 'isextern' flag passed to define_label() need
- * examining. This information may or may not tell us what we need to
- * know (ie should we be generating an import record at this point for this
- * label). If these aren't the semantics, the semantics should be changed
- * to this.
- *
- * The output module interface needs changing, so that the `isextern' flag
- * is passed to the module, so that it can be easily tested for.
- */
|