mirror of
https://github.com/elliotnunn/NetBoot.git
synced 2024-12-27 01:32:11 +00:00
486 lines
9.2 KiB
C
486 lines
9.2 KiB
C
/* 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;
|
|
}
|