/* symbol.c - manage all kinds of symbols */ /* (c) in 2014-2018 by Volker Barthelmann and Frank Wille */ #include "vasm.h" symbol *first_symbol=NULL; static symbol *saved_symbol=NULL; static char *last_global_label=emptystr; #ifndef SYMHTABSIZE #define SYMHTABSIZE 0x10000 #endif static hashtable *symhash; static hashtable *regsymhash; static void print_type(FILE *f,symbol *p) { static const char *typename[] = {"???","obj","func","sect","file"}; int t; if (p == NULL) ierror(0); t = TYPE(p); fprintf(f,"type=%s ",typename[t<=TYPE_LAST?t:0]); } void print_symbol(FILE *f,symbol *p) { if (p==NULL) ierror(0); /* this is usually an error in a cpu-backend, don't crash! */ fprintf(f,"%s ",p->name); if (p->type==LABSYM) fprintf(f,"LAB (0x%llx) ",ULLTADDR(p->pc)); if (p->type==IMPORT) fprintf(f,"IMP "); if (p->type==EXPRESSION){ fprintf(f,"EXPR("); print_expr(f,p->expr); fprintf(f,") "); } if (!(p->flags&(USED|VASMINTERN))) fprintf(f,"UNUSED "); if (p->flags&VASMINTERN) fprintf(f,"INTERNAL "); if (p->flags&EXPORT) fprintf(f,"EXPORT "); if (p->flags&COMMON) fprintf(f,"COMMON "); if (p->flags&WEAK) fprintf(f,"WEAK "); if (p->flags&LOCAL) fprintf(f,"LOCAL "); if (p->flags&PROTECTED) fprintf(f,"PROT "); if (p->flags&REFERENCED) fprintf(f,"REF "); if (p->flags&ABSLABEL) fprintf(f,"ABS "); if (p->flags&EQUATE) fprintf(f,"EQU "); if (p->flags®LIST) fprintf(f,"REGL "); if (TYPE(p)) print_type(f,p); if (p->size){ fprintf(f,"size="); print_expr(f,p->size); fprintf(f," "); } if (p->align) fprintf(f,"align=%lu ",(unsigned long)p->align); if (p->sec) fprintf(f,"sec=%s ",p->sec->name); } char *get_bind_name(symbol *p) { if (p->flags&EXPORT) return "global"; else if (p->flags&WEAK) return "weak"; else if (p->flags&LOCAL) return "local"; return "unknown"; } void add_symbol(symbol *p) { hashdata data; p->next = first_symbol; first_symbol = p; data.ptr = p; add_hashentry(symhash,p->name,data); } symbol *find_symbol(char *name) { hashdata data; if (!find_name(symhash,name,&data)) return 0; return data.ptr; } void refer_symbol(symbol *sym,char *refname) /* refer to an existing symbol with an additional name */ { hashdata data; data.ptr=sym; add_hashentry(symhash,refname,data); } void save_symbols(void) /* remember current list of symbols to be restored later */ { saved_symbol = first_symbol; } void restore_symbols(void) /* restore to a previously saved symbol list */ { symbol *firstprot=NULL, *lastprot=NULL; symbol *symp; if (saved_symbol) { while (first_symbol != saved_symbol) { symp = first_symbol; first_symbol = symp->next; if (symp->flags & PROTECTED) { /* keep this symbol */ symp->next = firstprot; firstprot = symp; if (!lastprot) lastprot = symp; } else { rem_hashentry(symhash,symp->name,nocase); /* myfree(symp->name); could be dangerous? */ myfree(symp); } } if (firstprot) { /* add protected symbols to the list again */ lastprot->next = first_symbol; first_symbol = firstprot; } saved_symbol = NULL; } } int check_symbol(char *name) /* issue an error when symbol is already defined in the current source */ { symbol *sym; if (sym = find_symbol(name)) { if (sym->type != IMPORT) { general_error(67,name); /* repeatedly defined symbol */ return 1; } } return 0; } char *set_last_global_label(char *name) { char *prevlgl = last_global_label; last_global_label = name; return prevlgl; } int is_local_label(char *name) /* returns true when name belong to a label with local scope */ { return *name == ' '; } char *make_local_label(char *glob,int glen,char *loc,int llen) /* construct a local label of the form: " " + global_label_name + " " + local_label_name */ { char *name,*p; if (glen == 0) { /* use the last defined global label */ glob = last_global_label; glen = strlen(last_global_label); } p = name = mymalloc(llen+glen+3); *p++ = ' '; if (glen) { memcpy(p,glob,glen); p += glen; } *p++ = ' '; memcpy(p,loc,llen); *(p + llen) = '\0'; return name; } symbol *new_abs(char *name,expr *tree) { symbol *new = find_symbol(name); int add; if (new) { if (new->flags&EQUATE) general_error(67,name); /* repeatedly defined symbol (error) */ if (new->type!=IMPORT && new->type!=EXPRESSION) general_error(5,name); /* symbol redefined (warning) */ add=0; } else { new = mymalloc(sizeof(*new)); new->name = mystrdup(name); add = 1; } new->type = EXPRESSION; new->sec = 0; new->expr = tree; if (add) { add_symbol(new); new->flags = 0; new->size = 0; new->align = 0; } return new; } symbol *new_equate(char *name,expr *tree) { symbol *sym; check_symbol(name); sym = new_abs(name,tree); sym->flags |= EQUATE; /* not allowed to change this absolute symbol */ return sym; } symbol *new_import(char *name) { symbol *new = find_symbol(name); if (new) return new; new = mymalloc(sizeof(*new)); new->type = IMPORT; new->flags = 0; new->name = mystrdup(name); new->sec = 0; new->pc = 0; new->size = 0; new->align = 0; add_symbol(new); return new; } symbol *new_labsym(section *sec,char *name) { symbol *new; int add; if (chklabels) { hashdata data; if (find_name_nc(mnemohash,name,&data)) general_error(39); /* name conflicts with mnemonic */ else if (find_name_nc(dirhash,name,&data)) general_error(40); /* name conflicts with directive */ } if (!sec) { sec = default_section(); if (!sec) { general_error(3); return new_import(name); } } sec->flags |= HAS_SYMBOLS; if (sec->flags&LABELS_ARE_LOCAL) name = make_local_label(sec->name,strlen(sec->name),name,strlen(name)); if (new = find_symbol(name)) { if (new->type!=IMPORT) { symbol *old = new; new = mymalloc(sizeof(*new)); *new = *old; general_error(74,name); /* label redefined (error) */ } add = 0; } else { new = mymalloc(sizeof(*new)); if (sec->flags&LABELS_ARE_LOCAL) new->name = name; else new->name = mystrdup(name); add = 1; } new->type = LABSYM; new->sec = sec; new->pc = sec->pc; if (add) { add_symbol(new); new->flags = 0; new->size = 0; new->align = 0; } if (*name != ' ') last_global_label = new->name; if (sec->flags & ABSOLUTE) new->flags |= ABSLABEL; else new->flags &= ~ABSLABEL; return new; } symbol *new_tmplabel(section *sec) { static unsigned long tmplabcnt = 0; char tmpnam[16]; sprintf(tmpnam," *tmp%09lu*",tmplabcnt++); return new_labsym(sec,tmpnam); } symbol *internal_abs(char *name) { symbol *new = find_symbol(name); if (new) { if (new->type!=EXPRESSION || (new->flags&(EXPORT|COMMON|WEAK))) general_error(37,name); /* internal symbol redefined by user */ } else { new = new_abs(name,number_expr(0)); new->flags |= VASMINTERN; } return new; } expr *set_internal_abs(char *name,taddr newval) { symbol *sym = internal_abs(name); expr *oldexpr = sym->expr; taddr oldval; if (oldexpr == NULL) ierror(0); eval_expr(oldexpr,&oldval,NULL,0); if (newval != oldval) sym->expr = number_expr(newval); return oldexpr; } #ifdef HAVE_REGSYMS void add_regsym(regsym *rsym) { hashdata data; data.ptr = rsym; add_hashentry(regsymhash,rsym->reg_name,data); } regsym *find_regsym(char *name,int len) { hashdata data; if (find_namelen(regsymhash,name,len,&data)) return data.ptr; return NULL; } regsym *find_regsym_nc(char *name,int len) { hashdata data; if (find_namelen_nc(regsymhash,name,len,&data)) return data.ptr; return NULL; } regsym *new_regsym(int redef,int no_case,char *name,int type, unsigned int flags,unsigned int num) { int len = strlen(name); regsym *rsym; /* check if register symbol already exists */ rsym = no_case!=0 ? find_regsym_nc(name,len) : find_regsym(name,len); if (rsym!=NULL && !redef) { general_error(58,name); /* register symbol redefined */ return rsym; } if (rsym == NULL) { rsym = mymalloc(sizeof(regsym)); rsym->reg_name = mystrdup(name); rsym->reg_type = type; rsym->reg_flags = flags; rsym->reg_num = num; add_regsym(rsym); } else { /* just update */ rsym->reg_type = type; rsym->reg_flags = flags; rsym->reg_num = num; } return rsym; } /* remove an already defined register symbol from the hash table */ int undef_regsym(char *name,int no_case,int type) { regsym *rsym = no_case!=0 ? find_regsym_nc(name,strlen(name)) : find_regsym(name,strlen(name)); if (rsym != NULL) { if (rsym->reg_type == type) { rem_hashentry(regsymhash,name,no_case); return 1; } else general_error(70,name); /* register symbol has wrong type */ } else general_error(69,name); /* register does not exist */ return 0; } #endif /* HAVE_REGSYMS */ int init_symbol(void) { symhash = new_hashtable(SYMHTABSIZE); #ifdef HAVE_REGSYMS regsymhash = new_hashtable(REGSYMHTSIZE); #endif return 1; }