/* symbols.c -symbol table- Copyright (C) 1987 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "as.h" #include "hash.h" #include "obstack.h" /* For "symbols.h" */ #include "struc-symbol.h" #include "symbols.h" #include "frags.h" #include "expr.h" #include "sections.h" #include "read.h" #include "xmalloc.h" #include "messages.h" #include "fixes.h" #include "input-scrub.h" /* symbol-name => struct symbol pointer */ struct hash_control *sy_hash = NULL; /* FixS & symbols live here */ struct obstack notes = { 0 }; /* all the symbol nodes */ symbolS *symbol_rootP = NULL; /* last struct symbol we made, or NULL */ symbolS *symbol_lastP = NULL; /* The last symbol we assigned an index to. */ symbolS *symbol_lastIndexedP = NULL; symbolS abs_symbol = { {{0}} }; /* * Un*x idea of local labels. They are made by "n:" where n * is any decimal digit. Refer to them with * "nb" for previous (backward) n: * or "nf" for next (forward) n:. * * Like Un*x AS, we have one set of local label counters for entire assembly, * not one set per (sub)segment like in most assemblers. This implies that * one can refer to a label in another segment, and indeed some crufty * compilers have done just that. * * I document the symbol names here to save duplicating words elsewhere. * The mth occurence of label n: is turned into the symbol "Ln^Am" where * n is a digit and m is a decimal number. "L" makes it a label discarded * unless debugging and "^A"('\1') ensures no ordinary symbol SHOULD get the * same name as a local label symbol. The first "4:" is "L4^A1" - the m * numbers begin at 1. */ typedef short unsigned int local_label_countT; static void make_stab_for_symbol( symbolS *symbolP); static void fb_label_init(void); void symbol_begin( void) { symbol_lastP = NULL; symbol_rootP = NULL; /* In case we have 0 symbols (!!) */ sy_hash = hash_new(); memset((char *)(&abs_symbol), '\0', sizeof(abs_symbol)); abs_symbol.sy_type = N_ABS; /* Can't initialise a union. Sigh. */ fb_label_init (); } /* Somebody else's idea of local labels. They are made by "n:" where n is any decimal digit. Refer to them with "nb" for previous (backward) n: or "nf" for next (forward) n:. We do a little better and let n be any number, not just a single digit, but since the other guy's assembler only does ten, we treat the first ten specially. Like someone else's assembler, we have one set of local label counters for entire assembly, not one set per (sub)segment like in most assemblers. This implies that one can refer to a label in another segment, and indeed some crufty compilers have done just that. Since there could be a LOT of these things, treat them as a sparse array. */ #define LOCAL_LABEL_CHAR '\002' #define FB_LABEL_SPECIAL (10) static int32_t fb_low_counter[FB_LABEL_SPECIAL]; static int32_t *fb_labels; static int32_t *fb_label_instances; static int32_t fb_label_count; static int32_t fb_label_max; /* This must be more than FB_LABEL_SPECIAL. */ #define FB_LABEL_BUMP_BY (FB_LABEL_SPECIAL + 6) static void fb_label_init (void) { memset ((void *) fb_low_counter, '\0', sizeof (fb_low_counter)); } /* Add one to the instance number of this fb label. */ void fb_label_instance_inc (int32_t label) { int32_t *i; if (label < FB_LABEL_SPECIAL) { ++fb_low_counter[label]; return; } if (fb_labels != NULL) { for (i = fb_labels + FB_LABEL_SPECIAL; i < fb_labels + fb_label_count; ++i) { if (*i == label) { ++fb_label_instances[i - fb_labels]; return; } /* if we find it */ } /* for each existing label */ } /* If we get to here, we don't have label listed yet. */ if (fb_labels == NULL) { fb_labels = (int32_t *) xmalloc (FB_LABEL_BUMP_BY * sizeof (int32_t)); fb_label_instances = (int32_t *) xmalloc (FB_LABEL_BUMP_BY * sizeof (int32_t)); fb_label_max = FB_LABEL_BUMP_BY; fb_label_count = FB_LABEL_SPECIAL; } else if (fb_label_count == fb_label_max) { fb_label_max += FB_LABEL_BUMP_BY; fb_labels = (int32_t *) xrealloc ((char *) fb_labels, fb_label_max * sizeof (int32_t)); fb_label_instances = (int32_t *) xrealloc ((char *) fb_label_instances, fb_label_max * sizeof (int32_t)); } /* if we needed to grow */ fb_labels[fb_label_count] = label; fb_label_instances[fb_label_count] = 1; ++fb_label_count; } static int32_t fb_label_instance (int32_t label) { int32_t *i; if (label < FB_LABEL_SPECIAL) { return (fb_low_counter[label]); } if (fb_labels != NULL) { for (i = fb_labels + FB_LABEL_SPECIAL; i < fb_labels + fb_label_count; ++i) { if (*i == label) { return (fb_label_instances[i - fb_labels]); } /* if we find it */ } /* for each existing label */ } /* We didn't find the label, so this must be a reference to the first instance. */ return 0; } /* Caller must copy returned name: we re-use the area for the next name. The mth occurence of label n: is turned into the symbol "Ln^Bm" where n is the label number and m is the instance number. "L" makes it a label discarded unless debugging and "^B"('\2') ensures no ordinary symbol SHOULD get the same name as a local label symbol. The first "4:" is "L4^B1" - the m numbers begin at 1. */ char * /* Return local label name. */ fb_label_name (int32_t n, /* We just saw "n:", "nf" or "nb" : n a number. */ int32_t augend /* 0 for nb, 1 for n:, nf. */) { int32_t i; /* Returned to caller, then copied. Used for created names ("4f"). */ static char symbol_name_build[24]; register char *p; register char *q; char symbol_name_temporary[20]; /* Build up a number, BACKWARDS. */ know (n >= 0); #ifdef TC_MMIX know ((uint32_t) augend <= 2 /* See mmix_fb_label. */); #else know ((uint32_t) augend <= 1); #endif p = symbol_name_build; #ifdef LOCAL_LABEL_PREFIX *p++ = LOCAL_LABEL_PREFIX; #endif *p++ = 'L'; /* Next code just does sprintf( {}, "%d", n); */ /* Label number. */ q = symbol_name_temporary; for (*q++ = 0, i = n; i; ++q) { *q = i % 10 + '0'; i /= 10; } while ((*p = *--q) != '\0') ++p; *p++ = LOCAL_LABEL_CHAR; /* ^B */ /* Instance number. */ q = symbol_name_temporary; for (*q++ = 0, i = fb_label_instance (n) + augend; i; ++q) { *q = i % 10 + '0'; i /= 10; } while ((*p++ = *--q) != '\0');; /* The label, as a '\0' ended string, starts at symbol_name_build. */ return (symbol_name_build); } /* Decode name that may have been generated by foo_label_name() above. If the name wasn't generated by foo_label_name(), then return it unaltered. This is used for error messages. */ char * decode_local_label_name (char *s) { char *p; char *symbol_decode; int label_number; int instance_number; char *type; int index = 0; #ifdef LOCAL_LABEL_PREFIX if (s[index] == LOCAL_LABEL_PREFIX) ++index; #endif if (s[index] != 'L') return s; for (label_number = 0, p = s + index + 1; isdigit (*p); ++p) label_number = (10 * label_number) + *p - '0'; if (*p == LOCAL_LABEL_CHAR) type = "fb"; else return s; for (instance_number = 0, p++; isdigit (*p); ++p) instance_number = (10 * instance_number) + *p - '0'; #define MESSAGE_FORMAT "\"%d\" (instance number %d of a %s label)" symbol_decode = obstack_alloc (¬es, strlen (MESSAGE_FORMAT) + 30); sprintf (symbol_decode, MESSAGE_FORMAT, label_number, instance_number, type); return symbol_decode; } void local_colon( int n) /* just saw "n:" */ { fb_label_instance_inc (n); colon (fb_label_name (n, 0), 1); } /* * symbol_new() * * Return a pointer to a new symbol. * Die if we can't make a new symbol. * Fill in the symbol's values. * Add symbol to end of symbol chain. * * * Please always call this to create a new symbol. * * Changes since 1985: Symbol names may not contain '\0'. Sigh. */ symbolS * symbol_new( char *name, /* We copy this: OK to alter your copy. */ unsigned char type, /* As in . */ char other, /* As in . */ short desc, /* As in . */ valueT value, /* As in , often an address. */ /* Often used as offset from frag address. */ struct frag *frag) /* For sy_frag. */ { register symbolS * symbolP; register char * preserved_copy_of_name; register unsigned int name_length; char * p; name_length = strlen(name) + 1; obstack_grow(¬es,name,name_length); p=obstack_finish(¬es); /* obstack_1done( ¬es, name, name_length, &p ); */ preserved_copy_of_name = p; p=obstack_alloc(¬es,sizeof(struct symbol)); /* obstack_1blank( ¬es, sizeof(struct symbol), &p ); */ symbolP = (symbolS *) p; symbolP -> sy_name = preserved_copy_of_name; symbolP -> sy_type = type; symbolP -> sy_other = other; symbolP -> sy_desc = desc; symbolP -> sy_value = value; symbolP -> sy_frag = frag; symbolP -> sy_prev_by_index = NULL; /* Don't know what this is yet. */ symbolP -> sy_has_been_resolved = 0; symbolP -> sy_next = NULL; /* End of chain. */ symbolP -> sy_forward = NULL; /* JF */ symbolP -> expression = NULL; #ifdef SUSPECT symbolP -> sy_name_offset = ~ 0; /* Impossible offset catches errors. */ symbolP -> sy_number = ~ 0; /* Ditto. */ #endif /* * Link to end of symbol chain. */ if (symbol_lastP) { symbol_lastP -> sy_next = symbolP; } else { symbol_rootP = symbolP; } symbol_lastP = symbolP; return (symbolP); } /* FROM line 136 */ symbolS * symbol_create (const char *name, /* It is copied, the caller can destroy/modify. */ segT segment, /* Segment identifier (SEG_). */ valueT valu, /* Symbol value. */ fragS *frag /* Associated fragment. */) { /* FIXME */ return symbol_new ((char *)name, 0, segment, 0, valu, frag); } /* * symbol_assign_index() * * Assigns the next index to the given symbol. * * Asserts that the symbol has not been assigned an index yet. * */ void symbol_assign_index( struct symbol *symbolP) { if (symbolP->sy_prev_by_index != NULL) { as_fatal("symbol %s already has an index", symbolP->sy_name); } symbolP->sy_prev_by_index = symbol_lastIndexedP; symbol_lastIndexedP = symbolP; } /* * colon() * * We have just seen ":". * Creates a struct symbol unless it already exists. * * Gripes if we are redefining a symbol incompatibly (and ignores it). * */ /* This is used to work around compiler optimizer bug #50416 */ static volatile unsigned int temp; void colon( /* just seen "x:" - rattle symbols & frags */ char *sym_name, /* symbol name, as a cannonical string */ /* We copy this string: OK to alter later. */ int local_colon)/* non-zero if called from local_colon() */ { register struct symbol * symbolP; /* symbol we are working with */ if (frchain_now == NULL) { know(flagseen['n']); as_fatal("with -n a section directive must be seen before assembly " "can begin"); } if (inlineasm_checks && local_colon == 0) { if (inlineasm_file_name) as_warn_where_with_column(inlineasm_file_name, inlineasm_line_number, inlineasm_column_number, "label definition in inlineasm"); else as_bad("label definition in inlineasm"); } if ((symbolP = symbol_table_lookup( sym_name ))) { /* * Now check for undefined symbols */ if ((symbolP -> sy_type & N_TYPE) == N_UNDF) { temp = symbolP->sy_desc; if( symbolP -> sy_other == 0 /* bug #50416 -O causes this not to work for: && ((symbolP->sy_desc) & (~REFERENCE_TYPE)) == 0 */ && (temp & (~(REFERENCE_TYPE | N_WEAK_REF | N_WEAK_DEF | N_ARM_THUMB_DEF | N_NO_DEAD_STRIP | REFERENCED_DYNAMICALLY))) == 0 && symbolP -> sy_value == 0) { symbolP -> sy_frag = frag_now; symbolP -> sy_value = obstack_next_free(& frags) - frag_now -> fr_literal; know( N_UNDF == 0 ); symbolP -> sy_type |= N_SECT; /* keep N_EXT bit */ symbolP -> sy_other = frchain_now->frch_nsect; symbolP -> sy_desc &= ~REFERENCE_TYPE; symbolP -> sy_desc &= ~N_WEAK_REF; symbol_assign_index(symbolP); #ifdef NeXT_MOD /* generate stabs for debugging assembly code */ if(flagseen['g']) make_stab_for_symbol(symbolP); #endif } else { as_fatal( "Symbol \"%s\" is already defined as \"%s\"/%d.%d." TA_DFMT ".", sym_name, seg_name [(int) N_TYPE_seg [symbolP -> sy_type & N_TYPE]], symbolP -> sy_other, symbolP -> sy_desc, symbolP -> sy_value); } } else { as_fatal("Symbol %s already defined.",sym_name); } } else { symbolP = symbol_new (sym_name, N_SECT, frchain_now->frch_nsect, 0, (valueT)(obstack_next_free(&frags)-frag_now->fr_literal), frag_now); symbol_table_insert (symbolP); symbol_assign_index (symbolP); #ifdef NeXT_MOD /* generate stabs for debugging assembly code */ if(flagseen['g']) make_stab_for_symbol(symbolP); #endif } #ifdef tc_frob_label tc_frob_label(symbolP); #endif } /* * symbol_table_insert() * * Die if we can't insert the symbol. * */ void symbol_table_insert( struct symbol *symbolP) { const char * error_string; know( symbolP ); know( symbolP -> sy_name ); error_string = hash_jam (sy_hash, symbolP -> sy_name, (char *)symbolP); if (error_string != NULL && error_string[0] != '\0') { as_fatal( "Inserting \"%s\" into symbol table failed: %s", symbolP -> sy_name, error_string); } } /* * symbol_find_or_make() * * If a symbol name does not exist, create it as undefined, and insert * it into the symbol table. Return a pointer to it. */ symbolS * symbol_find_or_make( char *name) { register symbolS * symbolP; symbolP = symbol_table_lookup (name); if (symbolP == NULL) { symbolP = symbol_new (name, N_UNDF, 0, 0, 0, & zero_address_frag); symbol_table_insert (symbolP); } return (symbolP); } /* * symbol_find() * * Implement symbol table lookup. * In: A symbol's name as a string: '\0' can't be part of a symbol name. * Out: NULL if the name was not in the symbol table, else the address * of a struct symbol associated with that name. */ symbolS * symbol_find( char *name) { return ( (symbolS *) hash_find( sy_hash, name )); } /* * symbol_table_lookup() * * Same as symbol_find() except assumes the symbol is being looked up and is * a non-lazy symbol reference. */ symbolS * symbol_table_lookup( char *name) { register symbolS * symbolP; symbolP = (symbolS *) hash_find( sy_hash, name ); if(symbolP != NULL) symbolP->sy_desc &= ~REFERENCE_FLAG_UNDEFINED_LAZY; return(symbolP); } #ifdef NeXT_MOD /* generate stabs for debugging assembly code */ /* * make_stab_for_symbol() is called when -g is present for a label that is * being defined. If the label is a text label and in the (__TEXT,__text) * section and not a local label create a stab for it. * * See the detailed comments about stabs in read_a_source_file() for a * description of what is going on here. */ static void make_stab_for_symbol( symbolS *symbolP) { symbolS *stab; int stabnamelen; char *stabname; if(symbolP->sy_name[0] == 'L') return; if((symbolP->sy_type & N_TYPE) != N_SECT) return; if(symbolP->sy_other != text_nsect) return; stabnamelen = strlen(symbolP->sy_name) + sizeof(":f3"); stabname = xmalloc(stabnamelen); strcpy(stabname, symbolP->sy_name); if(symbolP->sy_type & N_EXT) strcat(stabname, ":F3"); else strcat(stabname, ":f3"); stab = symbol_new( stabname, 36, /* N_FUN */ text_nsect, /* n_sect */ logical_input_line, /* n_desc, line number */ symbolP->sy_value, symbolP->sy_frag); free(stabname); } #endif /* NeXT generate stabs for debugging assembly code */ /* * indirect_symbol_new() * * Return a pointer to a new indirect_symbol. * Die if we can't make a new indirect_symbol. * Fill in the indirect_symbol's values. * Add symbol to end of section's indirect symbol chain. */ isymbolS * indirect_symbol_new( char *name, /* We copy this: OK to alter your copy. */ struct frag *frag, /* For sy_frag. */ uint32_t offset) /* Offset from frag address. */ { isymbolS *isymbolP; char *preserved_copy_of_name; uint32_t name_length; char *p; struct frag *fr_next; symbolS *symbolP; #ifdef CHECK_INDIRECTS uint32_t stride, fr_fix; #endif /* * First see if the last frag recorded for an indirect symbol turned * out to be zero sized then changed that recorded frag to the next * non-zero frag in the list. I think this happens because we record * the frag before we fill it and if we run out of space that frag gets * a zero size and a new one is created. */ if(frchain_now->frch_isym_last != NULL && frchain_now->frch_isym_last->isy_frag->fr_fix == 0){ if(frchain_now->frch_isym_last->isy_frag->fr_next != NULL){ fr_next = frchain_now->frch_isym_last->isy_frag->fr_next; while(fr_next->fr_fix == 0 && fr_next->fr_type == rs_fill && fr_next->fr_next != NULL) fr_next = fr_next->fr_next; frchain_now->frch_isym_last->isy_frag = fr_next; } } name_length = strlen(name) + 1; obstack_grow(¬es, name, name_length); p = obstack_finish(¬es); preserved_copy_of_name = p; p = obstack_alloc(¬es, sizeof(struct indirect_symbol)); isymbolP = (isymbolS *)p; isymbolP->isy_name = preserved_copy_of_name; isymbolP->isy_offset = offset; isymbolP->isy_frag = frag; isymbolP->isy_next = NULL; /* End of chain. */ isymbolP->isy_symbol = NULL; /* * Link to end of indirect symbol chain and check for missing indirect * symbols. */ if(frchain_now->frch_isym_root == NULL){ #ifdef CHECK_INDIRECTS if(offset != 0) as_bad("missing or bad indirect symbol for section (%s,%s)", frchain_now->frch_section.segname, frchain_now->frch_section.sectname); #endif frchain_now->frch_isym_root = isymbolP; frchain_now->frch_isym_last = isymbolP; } else{ #ifdef CHECK_INDIRECTS if((frchain_now->frch_section.flags & SECTION_TYPE) == S_SYMBOL_STUBS) stride = frchain_now->frch_section.reserved2; else stride = sizeof(uint32_t); if(frag == frchain_now->frch_isym_last->isy_frag){ if(offset - frchain_now->frch_isym_last->isy_offset != stride) as_bad("missing or bad indirect symbol for section " "(%s,%s)", frchain_now->frch_section.segname, frchain_now->frch_section.sectname); } else{ if(frchain_now->frch_isym_last->isy_frag->fr_fix < stride){ fr_fix = 0; fr_next = frchain_now->frch_isym_last->isy_frag; while(fr_fix + fr_next->fr_fix < stride && fr_next->fr_type == rs_fill && fr_next->fr_next != NULL){ fr_fix += fr_next->fr_fix; fr_next = fr_next->fr_next; } if(frag != fr_next->fr_next || fr_fix + fr_next->fr_fix != stride || offset != 0) as_bad("missing or bad indirect symbol for section " "(%s,%s)", frchain_now->frch_section.segname, frchain_now->frch_section.sectname); } else{ fr_next = frchain_now->frch_isym_last->isy_frag->fr_next; /* * Because of section changes there maybe some zero length * frags after the last one that passed through here. So * skip them and get to the last real one. */ while(fr_next->fr_fix == 0 && fr_next->fr_type == rs_fill && fr_next->fr_next != NULL) fr_next = fr_next->fr_next; if(frag != fr_next || offset != 0) as_bad("missing or bad indirect symbol for section " "(%s,%s)", frchain_now->frch_section.segname, frchain_now->frch_section.sectname); } } #endif frchain_now->frch_isym_last->isy_next = isymbolP; frchain_now->frch_isym_last = isymbolP; } if((frchain_now->frch_section.flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS){ symbolP = (symbolS *)hash_find(sy_hash, name); if(symbolP != NULL) symbolP->sy_desc &= ~REFERENCE_FLAG_UNDEFINED_LAZY; } return(isymbolP); } const char * S_GET_NAME (symbolS *s) { return s->sy_name; } int S_IS_DEFINED (symbolS *s) { return (s->sy_type & N_TYPE) != N_UNDF; } /* FROM line 2317 */ #ifdef TC_SYMFIELD_TYPE /* Get a pointer to the processor information for a symbol. */ TC_SYMFIELD_TYPE * symbol_get_tc (symbolS *s) { return &s->sy_tc; } /* Set the processor information for a symbol. */ void symbol_set_tc (symbolS *s, TC_SYMFIELD_TYPE *o) { s->sy_tc = *o; } #endif /* TC_SYMFIELD_TYPE */ int S_IS_LOCAL (symbolS *s) { const char *name; name = S_GET_NAME (s); if(name == NULL) return(1); if(name[0] == 'L' && flagseen['L'] == FALSE) return(1); else return(0); } fragS * symbol_get_frag (symbolS *s) { return(s->sy_frag); } /* * symbol_temp_new(), symbol_temp_new_now() are used by dwarf2dbg.c to make * symbols in dwarf sections. symbol_temp_make() is used to make an undefined * symbol that its values are later set by symbol_set_value_now() to the current * address in a dwarf section. * * These are used in expressions, so the expression values can be put out in * dwarf section contents. */ symbolS * symbol_temp_new( segT nsect, valueT value, struct frag *frag) { return(symbol_new(FAKE_LABEL_NAME, N_SECT, nsect, 0, value, frag)); } symbolS * symbol_temp_new_now(void) { return(symbol_temp_new(now_seg, frag_now_fix(), frag_now)); } symbolS * symbol_temp_make(void) { return(symbol_new(FAKE_LABEL_NAME, N_UNDF, 0, 0, 0, & zero_address_frag)); } /* Set the value of SYM to the current position in the current segment. */ void symbol_set_value_now( symbolS *sym) { sym->sy_type = N_SECT; sym->sy_other = now_seg; sym->sy_value = frag_now_fix(); sym->sy_frag = frag_now; } /* end: symbols.c */