#ident "$Header: sym.c 1.5 89/08/07 $"

/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

/*
 * $Log:	sym.c,v $
 */

#include "crash.h"

#include <sys/types.h>
#ifdef CROSS
#include "/usr/include/sys/stat.h"
#else
#include <sys/stat.h>
#endif /* CROSS */
#include <a.out.h>

#include <ldfcn.h>

#define MIN(a,b)	((a)>(b)?(b):(a))
#define streq(s1, s2)   (strcmp(s1, s2) == 0)


/* Tune these for speed */
#define NUM_STE	 (8*BUFSIZ/sizeof (struct nlist))	/* 8k reads */
#define NUM_SYM	 5000
#define NUM_SDB	 5000

char *bsearch();
char *save_name();
static	struct	sym *symtable, **symtablepv, **symtablepn, *searchbyval();
static	struct	sdb *sdbtable, **sdbtablepn;
static	int cmpsymbyval(), cmpsymbynam(), cmpsdbbynam();
static	char *stringtable;


/* static	struct exec exec; */
static	int total_symbols, glb_symbols, sdb_symbols;

struct	sdb sdbinfo;

/* COFF support */

static LDFILE *ldptr;		/* load file pointer */
static FILHDR filehead;		/* file header */

static short sect_text;		/* text section number */
static short sect_data;		/* data section number */
static short sect_bss;		/* bss  section number */

long	text_size;
long	text_offset;
long	text_start;
long	data_size;
long	data_offset;
long	data_start;
long	bss_size;
long	bss_start;


getsymboltable()
{
	ldptr = NULL;	/* initialize so it will open file name */
	if ((ldptr = ldopen(dynixV, ldptr)) == NULL) {
		printf("Error: ldaopen(%s,) failed\n", dynixV);
		exit(1);
	}

	check_file_type();
	scan_sections();

	process();
	check_dup(0);

}

process()
{
	long num_syms;		/* number of symbols in file */
	long symindex;		/* symbol index as we process them */
	SYMENT sym;		/* structure to hold current symbol */
	char *sname;		/* symbol name */
	char *ldgetname();
	register int n;

	num_syms = HEADER(ldptr).f_nsyms;
	SANITY((num_syms < 0), "negative symbol count", "");
	if (num_syms == 0) {
		printf("Warning: no symbols\n");
		return;
	}

	for (symindex = 0; symindex < num_syms; symindex++) {
		if (ldtbread(ldptr, symindex, &sym) != SUCCESS) {
			printf("Error reading symbol %d\n", symindex);
			return;
		}
		if ((sname = ldgetname(ldptr, &sym)) == NULL) {
			symindex += sym.n_numaux;
			continue;
		}
		total_symbols++;
		if ( (sym.n_scnum == sect_text) ||
		     (sym.n_scnum == sect_data) ||
		     (sym.n_scnum == sect_bss)  ||
		     (sym.n_scnum == -1      )  || 		/* abs syms */
		     (sym.n_scnum == -2      )      ) {		/* debug syms */
			sym_process(sname, &sym, symindex);
		}
		symindex += sym.n_numaux;
	}

	/* allocate memory for for arrays of pointers */
	symtablepn = (struct sym **) malloc ( (unsigned) glb_symbols * sizeof (struct sym *) );
	SANITY(symtablepn == NULL, "malloc failed", "");
	symtablepv = (struct sym **) malloc ( (unsigned) glb_symbols * sizeof (struct sym *) );
	SANITY(symtablepv == NULL, "malloc failed", "");
	sdbtablepn = (struct sdb **) malloc ( (unsigned) sdb_symbols * sizeof (struct sdb *) );
	SANITY(sdbtablepn == NULL, "malloc failed", "");

	/* set all pointers before sorting them */
	for (n=0; n < glb_symbols; n++) {
		symtablepn[n] = symtablepv[n] = &symtable[n];
		if (debug[1])
		  printf("symtable[%d] = '%s' @ 0x%x\n", n,
		     symtable[n].sym_name, symtable[n].sym_value);
	}
	for (n=0; n < sdb_symbols; n++) {
		sdbtablepn[n] = &sdbtable[n];
		if (debug[2])
		  printf("sdbtable[%d] = '%s', type %d, desc %d, off %d, size %d\n",
		    n, sdbtable[n].sdb_name, sdbtable[n].sdb_type,
		    sdbtable[n].sdb_desc, sdbtable[n].sdb_soff,
		    sdbtable[n].sdb_size);
	}

	/* sort global symbols by name and value, sdb by name only */
	qsort((char *)symtablepn, glb_symbols, sizeof (struct sym *), cmpsymbynam);
	qsort((char *)symtablepv, glb_symbols, sizeof (struct sym *), cmpsymbyval);
	qsort((char *)sdbtablepn, sdb_symbols, sizeof (struct sdb *), cmpsdbbynam);

	if (debug[0])
	    printf("%d total symbols (%d=global, %d=sdb) %d%% useful\n", 
		total_symbols, glb_symbols, sdb_symbols, 
		((glb_symbols+sdb_symbols)*100)/total_symbols);
	
	if (debug[3]) {
	    for (n = 0; n < glb_symbols; n++)
		if (debug[3])
		    printf("symtablepn[%d] = '%s' @ 0x%x\n", n, 
		        (*(symtablepn[n])).sym_name, (*(symtablepn[n])).sym_value);
	}
	if (debug[3]) {
	    for (n = 0; n < glb_symbols; n++)
		if (debug[3])
		    printf("symtablepv[%d] = 0x%x  =  '%s'\n", n, 
		        (*(symtablepv[n])).sym_value, (*(symtablepv[n])).sym_name);
	}
	if (debug[4]) {
	    for (n = 0; n < sdb_symbols; n++)
		if (debug[4])
		    printf("sdbtablepn[%d] = '%s' off %d\n", n,
		        (*(sdbtablepn[n])).sdb_name, (*(sdbtablepn[n])).sdb_soff);
	}
}

sym_process(sname, sym, symindex)
	char *sname;
	SYMENT *sym;
	long symindex;
{
	char *saved_name;
	char *p;
	static warn_ustatic;

	/* Remove ".text", ".data", and ".bss" */
	if (streq(".text", sname))
		return;
	if (streq(".data", sname))
		return;
	if (streq(".bss", sname))
		return;
	if (streq("rcsid", sname))
		return;

	/* Remove local symbols such as "L9.$1" */
	p = sname;
	if (*p == 'L') {
		p++;
		if (*p >= '0' && *p <= '9') {
			p++;
			if (*p == '.') {
				p++;
				if (*p == '$')
					return;
			}
		}
	}

	switch (sym->n_sclass) {
	case C_EXT:
	case C_STAT:
		saved_name = save_name(sname);
		sym_add(saved_name, sym->n_value);
/*		sym_add_dbg(saved_name, sym, symindex); 	*/
		break;
	case C_MOS:
	case C_STRTAG:
	case C_MOU:
	case C_UNTAG:
		saved_name = save_name(sname);
		sym_add_dbg(saved_name, sym, symindex);
		break;
	case C_USTATIC:
		if (warn_ustatic == 0)
		    printf("Warning: don't know how to deal with C_USTATIC: '%s'\n",
		    sname);
		warn_ustatic = 1;
		break;
	default:
		break;
	}
}

/*
 * Historical note:
 *
 * cmpsymvyval used to do a simple comparison of values like so:
 *	return  (*a)->sym_value - (*b)->sym_value; 
 *
 * This does not work if the high bits are set for symbol values.  This did
 * not seem to occur with BSD a.out files, but it does with COFF files.
 * This is strange, because using "nm" on BSD /unix shows "_l" as an absolute
 * value with the high bit set.  But crash shows it with value 0 for BSD
 * version.  Correct value shows up for COFF version and this broke compare.
 */

static int
cmpsymbyval(a, b)
	struct sym **a, **b;
{
	int r;

	if (debug[5]) {
	    printf("cmpsymbyVAL: '%s' @ 0x%x  ...  '%s' @ 0x%x  : ",
		(*a)->sym_name, (*a)->sym_value,
		(*b)->sym_name, (*b)->sym_value);
	    if ( (*a)->sym_value > (*b)->sym_value ) {
		r = 1;
	    } else if ( (*a)->sym_value == (*b)->sym_value ) {
		r = 0;
	    } else
		r = -1;
	    if (r < 0)
		printf("<\n");
	    else if (r > 0)
		printf(">\n");
	    else
		printf("=\n");
	}

	if ( (*a)->sym_value > (*b)->sym_value )
		return(1);
	
	if ( (*a)->sym_value == (*b)->sym_value )
		return(0);

	return(-1);

/*	return  (*a)->sym_value - (*b)->sym_value; */
}

static int
cmpsymbynam(a, b)
	struct sym **a, **b;
{
	register char *s1, *s2;
	int r;

	/* expanded strcmp() */
	s1 = (*a)->sym_name;
	s2 = (*b)->sym_name;
	if (debug[6]) {
	    printf("cmpsymbyNAM: '%s' @ 0x%x  ...  '%s' @ 0x%x  : ",
		(*a)->sym_name, (*a)->sym_value,
		(*b)->sym_name, (*b)->sym_value);
	    while (*s1 == *s2++)
		if (*s1++ == '\0') {
		    printf("=\n");
		    goto pdone;
		}
	    r = (*s1 - *--s2);
	    if (r < 0)
		printf("<\n");
	    else if (r > 0)
		printf(">\n");
pdone:
	    s1 = (*a)->sym_name;
	    s2 = (*b)->sym_name;
	}

	while (*s1 == *s2++)
		if (*s1++ == '\0')
			return 0;
	return (*s1 - *--s2);
}

static int
cmpsdbbynam(a, b)
	struct sdb **a, **b;
{ 
	register char *s1, *s2;
	int r;

	/* expanded strcmp() */
	s1 = (*a)->sdb_name;
	s2 = (*b)->sdb_name;
	if (debug[7]) {
	    printf("cmpSDBbyNAM: '%s' @ 0x%x  ...  '%s' @ 0x%x  : ",
		(*a)->sdb_name, (*a)->sdb_name,
		(*b)->sdb_name, (*b)->sdb_name);
	    while (*s1 == *s2++)
		if (*s1++ == '\0') {
		    printf("=\n");
		    goto pdone;
		}
	    r = (*s1 - *--s2);
	    if (r < 0)
		printf("<\n");
	    else if (r > 0)
		printf(">\n");
pdone:
	    s1 = (*a)->sdb_name;
	    s2 = (*b)->sdb_name;
	}

	while (*s1 == *s2++)
		if (*s1++ == '\0')
			return 0;
	return (*s1 - *--s2);
}

sym_add(s, v)
	char *s;
	long int v;
{
	static struct sym *syme, *symp;

	if (symp == NULL || symp >= syme) {
		unsigned size = (unsigned)syme - (unsigned)symtable + (NUM_SYM * sizeof (struct sym));
		if (symp == NULL) {
			symtable = (struct sym *) malloc ( size );
			if (debug[10])
			    printf("sym_add: malloc\n");
		} else {
			symtable = (struct sym *) realloc( (char *) symtable, size );
			if (debug[10])
			    printf("sym_add: realloc\n");
		}
		SANITY(symtable == NULL, "malloc for ste symbol table failed", "");
		size /= sizeof (struct sym);
		symp = &symtable[ size - NUM_SYM ];
		syme = &symtable[ size ];
	}
	if (debug[8]) {
		printf("sym_add(%s, 0x%x)\n", s, v);
	}
	symp->sym_name = s;
	symp->sym_value = v;
	++glb_symbols;
	++symp;
}

sym_add_dbg(s, sym, symindex)
	char *s;
	SYMENT *sym;
	long symindex;
{
	static struct sdb *sdbe, *sdbp;
	AUXENT aux;
	int i;
	int  m = 0;

	if (debug[13]) {
		i = 0;
		while (i < MAX_CHECK && *name_buf[i] != '\0') {
			if (streq(s, name_buf[i])) {
				m = 1;
				break;
			}
			i++;
		}
	}

	if (sdbp == NULL || sdbp >= sdbe) {
		unsigned size = (unsigned)sdbe - (unsigned)sdbtable + (NUM_SDB * sizeof (struct sdb));
		if (sdbp == NULL) {
			sdbtable = (struct sdb *) malloc ( size );
			if (debug[10])
			    printf("sym_add_dbg: malloc\n");
		} else {
			sdbtable = (struct sdb *) realloc( (char *)sdbtable, size );
			if (debug[10])
			    printf("sym_add_dbg: realloc\n");
		}
		SANITY(sdbtable == NULL, "malloc for sdb symbol table failed", "");
		size /= sizeof (struct sdb);
		sdbp = &sdbtable[ size - NUM_SDB ];
		sdbe = &sdbtable[ size ];
	}

	if (m) {
	    printf("found sdb symbol '%s'\n", s);
	    printf("symbol class is %d = ", sym->n_sclass);
	    switch (sym->n_sclass) {
	    case C_EXT:
		printf("C_EXT\n");
		break;
	    case C_STAT:
		printf("C_STAT\n");
		break;
	    case C_MOS:
		printf("C_MOS\n");
		break;
	    case C_STRTAG:
		printf("C_STRTAG\n");
		break;
	    case C_MOU:
		printf("C_MOU\n");
		break;
	    case C_UNTAG:
		printf("C_UNTAG\n");
		break;
	    default:
		printf("???\n");
	    }

	}

	sdbp->sdb_name = s;
	sdbp->sdb_desc = sym->n_type;
	sdbp->sdb_soff = sym->n_value;
	switch (BTYPE(sym->n_type)) {
	case T_NULL:
	case T_ARG:
	case T_STRUCT:
	case T_UNION:
	case T_ENUM:
	case T_MOE:
	case T_CHAR:
	case T_UCHAR:
		sdbp->sdb_size = 1;
		break;
	case T_SHORT:
	case T_USHORT:
		sdbp->sdb_size = 2;
		break;
	case T_INT:
	case T_LONG:
	case T_FLOAT:
	case T_UINT:
	case T_ULONG:
		sdbp->sdb_size = 4;
		break;
	case T_DOUBLE:
		sdbp->sdb_size = 8;
	default:			/* can't happen ! */
		sdbp->sdb_size = 1;
		break;
	}
	switch (sym->n_sclass) {
	case C_EXT:
	case C_STAT:
		sdbp->sdb_type = N_GSYM;
		break;
	case C_MOS:
	case C_STRTAG:
	case C_MOU:
	case C_UNTAG:
		sdbp->sdb_type = N_SSYM;
		break;
	default:
		printf("WARNING: sym_add_dbg: don't know about class %d\n",
		    sym->n_sclass);
		return;
	}

	if (m) {
		printf("set type to 0x%x = %d\n",
		    sdbp->sdb_type, sdbp->sdb_type);
		printf("set desc to sym->n_type = 0x%x = 0%o = %d\n",
		    sym->n_type, sym->n_type, sym->n_type);
		printf("set soff to sym->n_value = 0x%x = %d\n",
		    sym->n_value, sym->n_value);
		printf("Set size to %d\n", sdbp->sdb_size);
	}

	/* See if we can pick up some size info from aux entries. */

	switch (sym->n_sclass) {
	    case C_EXT:
	    case C_MOS:
	    case C_STRTAG:
	    case C_MOU:
	    case C_UNTAG:
	    if (sym->n_numaux > 0) {
		if (ldtbread(ldptr, symindex + 1, &aux) != SUCCESS) {
		    printf("Warning: aux record read failed for '%s'\n", s);
		} else {
		    sdbp->sdb_size = aux.x_sym.x_misc.x_lnsz.x_size;
		    if (m)
			printf("Aux record gives size of %d\n", sdbp->sdb_size);
		}
	    } else {
		if (m)
		    printf("No aux records for this symbol\n");
	    }
	}
	 
	if (debug[9] || m)
	    printf("kept '%s', type=%#x, desc=%#x, soff=%#x\n",
		sdbp->sdb_name, sdbp->sdb_type, sdbp->sdb_desc, sdbp->sdb_soff);
	++sdb_symbols;
	++sdbp;
	if (m)
		printf("\n");
}



/*
 * Search routines
 */

/* check for duplicate names */

look_dupname(s)
	char *s;
{
	struct sym lsym, *p = &lsym, **pp;
	struct sym **p1, **p2;
	char buf[ MAXSTRLEN + 1 ];
	int loc, i;
	int count = 0;

	if (glb_symbols == 0)
		return(count);

	p->sym_name = s;
	pp = (struct sym **) bsearch((char *)&p, (char *)symtablepn, glb_symbols, sizeof (struct sym *), cmpsymbynam);
	if (pp == NULL)  {	/* try '_'name */
		buf [ 0 ] = '_';
		strcpy(&buf[1], s);
		p->sym_name = buf;
		pp = (struct sym **) bsearch((char *)&p, (char *)symtablepn, glb_symbols, sizeof (struct sym *), cmpsymbynam);
		if (pp == NULL) {
		    if (debug[11])
			printf("look_dupname('%s'): didn't find name\n", s);
		    return (count);
		}
	}

	p1 = NULL;
	p2 = NULL;
	loc = (int) (((char *) pp - (char *) symtablepn) / sizeof (struct sym *));
	count = 1;

	if (debug[11])
	    printf("look_dupname('%s'): binary search found name at loc %d\n",
		s, loc);

	if ((char *) pp == (char *) symtablepn) {
	    if (debug[11])
	    i = loc + 1;
	    while (p2 != &symtablepn[glb_symbols]) {
		p2 = &symtablepn[i];
		if (streq((*p2)->sym_name, s)) {
		    if ((*p2)->sym_value != (*pp)->sym_value)
		        count++;
		    if (debug[11])
			printf("compare with #%d '%s' matches\n", i, (*p2)->sym_name);
		} else {
		    if (debug[11])
			printf("compare with #%d '%s' fails\n", i, (*p2)->sym_name);
		    break;
		}
		i++;
	    }
	} else if ((char *) pp == (char *) &symtablepn[glb_symbols]) {
	    i = loc - 1;
	    while (p1 != symtablepn) {
		p1 = &symtablepn[i];
		if (streq((*p1)->sym_name, s)) {
		    if ((*p1)->sym_value != (*pp)->sym_value)
		        count++;
		    if (debug[11])
			printf("compare with #%d '%s' matches\n", i, (*p1)->sym_name);
		} else {
		    if (debug[11])
			printf("compare with #%d '%s' fails\n", i, (*p1)->sym_name);
		    break;
		}
		i--;
	    }
	} else {
	    i = loc + 1;
	    while (p2 !=  &symtablepn[glb_symbols]) {
		p2 = &symtablepn[i];
		if (streq((*p2)->sym_name, s)) {
		    if ((*p2)->sym_value != (*pp)->sym_value)
		        count++;
		    if (debug[11])
			printf("compare with #%d '%s' matches\n", i, (*p2)->sym_name);
		} else {
		    if (debug[11])
			printf("compare with #%d '%s' fails\n", i, (*p2)->sym_name);
		    break;
		}
		i++;
	    }
	    i = loc - 1;
	    while (p1 != symtablepn) {
		p1 = &symtablepn[i];
		if (streq((*p1)->sym_name, s)) {
		    if ((*p1)->sym_value != (*pp)->sym_value)
		        count++;
		    if (debug[11])
			printf("compare with #%d '%s' matches\n", i, (*p1)->sym_name);
		} else {
		    if (debug[11])
			printf("compare with #%d '%s' fails\n", i, (*p1)->sym_name);
		    break;
		}
		i--;
	    }
	}

	return(count);
}


/* check for duplicate sdb names */


look_dupsdb(s)
	char *s;
{
	struct sdb lsym, *p = &lsym, **pp;
	struct sdb **p1, **p2;
	char buf[ MAXSTRLEN + 1 ];
	int loc, i;
	int count = 0;

	if (sdb_symbols == 0)
		return(count);

	p->sdb_name = s;
	pp = (struct sdb **) bsearch((char *)&p, (char *)sdbtablepn, sdb_symbols, sizeof (struct sdb *), cmpsymbynam);
	if (pp == NULL)  {	/* try '_'name */
		buf [ 0 ] = '_';
		strcpy(&buf[1], s);
		p->sdb_name = buf;
		pp = (struct sdb **) bsearch((char *)&p, (char *)sdbtablepn, sdb_symbols, sizeof (struct sdb *), cmpsymbynam);
		if (pp == NULL) {
		    if (debug[12])
			printf("look_dupsdb('%s'): didn't find name\n", s);
		    return (count);
		}
	}

	p1 = NULL;
	p2 = NULL;
	loc = (int) (((char *) pp - (char *) sdbtablepn) / sizeof (struct sdb *));
	count = 1;

	if (debug[12])
	    printf("look_dupsdb('%s'): binary search found name at loc %d\n",
		s, loc);

	if ((char *) pp == (char *) sdbtablepn) {
	    i = loc + 1;
	    while (p2 != &sdbtablepn[sdb_symbols]) {
		p2 = &sdbtablepn[i];
		if (streq((*p2)->sdb_name, s)) {
		    if ((*p2)->sdb_type != (*pp)->sdb_type ||
		        (*p2)->sdb_desc != (*pp)->sdb_desc ||
		        (*p2)->sdb_soff != (*pp)->sdb_soff ||
		        (*p2)->sdb_size != (*pp)->sdb_size    )
		    	    count++;
		    if (debug[12])
			printf("compare with #%d '%s' matches\n", i, (*p2)->sdb_name);
		} else {
		    if (debug[12])
			printf("compare with #%d '%s' fails\n", i, (*p2)->sdb_name);
		    break;
		}
		i++;
	    }
	} else if ((char *) pp == (char *) &sdbtablepn[sdb_symbols]) {
	    i = loc - 1;
	    while (p1 != sdbtablepn) {
		p1 = &sdbtablepn[i];
		if (streq((*p1)->sdb_name, s)) {
		    if ((*p1)->sdb_type != (*pp)->sdb_type ||
		        (*p1)->sdb_desc != (*pp)->sdb_desc ||
		        (*p1)->sdb_soff != (*pp)->sdb_soff ||
		        (*p1)->sdb_size != (*pp)->sdb_size    )
		    	    count++;
		    if (debug[12])
			printf("compare with #%d '%s' matches\n", i, (*p1)->sdb_name);
		} else {
		    if (debug[12])
			printf("compare with #%d '%s' fails\n", i, (*p1)->sdb_name);
		    break;
		}
		i--;
	    }
	} else {
	    i = loc + 1;
	    while (p2 !=  &sdbtablepn[sdb_symbols]) {
		p2 = &sdbtablepn[i];
		if (streq((*p2)->sdb_name, s)) {
		    if ((*p2)->sdb_type != (*pp)->sdb_type ||
		        (*p2)->sdb_desc != (*pp)->sdb_desc ||
		        (*p2)->sdb_soff != (*pp)->sdb_soff ||
		        (*p2)->sdb_size != (*pp)->sdb_size    )
		    	    count++;
		    if (debug[12])
			printf("compare with #%d '%s' matches\n", i, (*p2)->sdb_name);
		} else {
		    if (debug[12])
			printf("compare with #%d '%s' fails\n", i, (*p2)->sdb_name);
		    break;
		}
		i++;
	    }
	    i = loc - 1;
	    while (p1 != sdbtablepn) {
		p1 = &sdbtablepn[i];
		if (streq((*p1)->sdb_name, s)) {
		    if ((*p1)->sdb_type != (*pp)->sdb_type ||
		        (*p1)->sdb_desc != (*pp)->sdb_desc ||
		        (*p1)->sdb_soff != (*pp)->sdb_soff ||
		        (*p1)->sdb_size != (*pp)->sdb_size    )
		    	    count++;
		    if (debug[12])
			printf("compare with #%d '%s' matches\n", i, (*p1)->sdb_name);
		} else {
		    if (debug[12])
			printf("compare with #%d '%s' fails\n", i, (*p1)->sdb_name);
		    break;
		}
		i--;
	    }
	}

	return(count);
}

check_dup(f)
	int f;
{
	struct sym **p1, **p2;
	struct sdb **p3, **p4;
	int count;
	int w = 0;

	if (glb_symbols < 2)
		return;
	p1 = &symtablepn[0];
	p2 = &symtablepn[1];

	while (p2 < &symtablepn[glb_symbols]) {
	    if (streq((*p1)->sym_name, (*p2)->sym_name)) {
		if ((*p1)->sym_value == (*p2)->sym_value) {
			p1 = p2;
			p2++;
			continue;
		}
		count = 2;
		if (f) {
		    printf("Dup names '%s', addr = 0x%x,  0x%x\n",
			(*p1)->sym_name, (*p1)->sym_value, (*p2)->sym_value);
		} else {
		    if (! w) {
			printf("Warning: duplicate symbols: ");
			w = 29;
		    }
		    if (w + strlen((*p1)->sym_name) > 79) {
			printf("\n  ");
			w = 3;
		    }
		    printf("%s, ", (*p1)->sym_name);
		    w += strlen((*p1)->sym_name) + 2;
		}
		p2++;
		while (streq((*p1)->sym_name, (*p2)->sym_name)) {
		    count++;
		    p2++;
		    if (p2 >= &symtablepn[glb_symbols])
			break;
		}
		p1 = p2;
		p2++;
	    }
	    p1++;
	    p2++;
	}
	if (w) {
	    printf("\n");
	    w = 0;
	}


	p1 = &symtablepv[0];
	p2 = &symtablepv[1];

	while (p2 < &symtablepv[glb_symbols]) {
	    if ((*p1)->sym_value == (*p2)->sym_value) {
		if ((*p1)->sym_value == (*p2)->sym_value) {
			p1 = p2;
			p2++;
			continue;
		}
		count = 2;
		if (f) {
		    printf("Dup values addr = 0x%x, sym '%s',  '%s'\n",
			(*p1)->sym_value, (*p1)->sym_name, (*p2)->sym_name);
		} else {
		    if (! w) {
			printf("Warning: duplicate address values: ");
			w = 37;
		    }
		    if (w + strlen((*p1)->sym_name) > 79) {
			printf("\n  ");
			w = 3;
		    }
		    printf("%s, ", (*p1)->sym_name);
		    w += strlen((*p1)->sym_name) + 2;
		}
		p2++;
		while ((*p1)->sym_value == (*p2)->sym_value) {
		    count++;
		    p2++;
		    if (p2 >= &symtablepv[glb_symbols])
			break;
		}
		p1 = p2;
		p2++;
	    }
	    p1++;
	    p2++;
	}
	if (w) {
	    printf("\n");
	    w = 0;
	}

	if (sdb_symbols < 2)
		return;

	p3 = &sdbtablepn[0];
	p4 = &sdbtablepn[1];

	while (p4 < &sdbtablepn[sdb_symbols]) {
	    if (streq((*p3)->sdb_name, (*p4)->sdb_name)) {
		if ((*p3)->sdb_type == (*p4)->sdb_type &&
		    (*p3)->sdb_desc == (*p4)->sdb_desc &&
		    (*p3)->sdb_soff == (*p4)->sdb_soff &&
		    (*p3)->sdb_size == (*p4)->sdb_size    ) {
			p3 = p4;
			p4++;
			continue;
		}
		count = 2;
		if (f) {
		    printf("Dup names '%s', addr = 0x%x,  0x%x\n",
			(*p3)->sdb_name, (*p3)->sdb_soff, (*p4)->sdb_soff);
		} else {
		    if (! w)  {
			printf("Warning: duplicate debug info: ");
			w = 33;
		    }
		    if (w + strlen((*p3)->sdb_name) > 79) {
			printf("\n  ");
			w = 3;
		    }
		    printf("%s, ", (*p3)->sdb_name);
		    w += strlen((*p3)->sdb_name) + 2;
		}
		p4++;
		while (streq((*p3)->sdb_name, (*p4)->sdb_name)) {
		    count++;
		    p4++;
		    if (p4 >= &sdbtablepn[sdb_symbols])
			break;
		}
		p3 = p4;
		p4++;
	    }
	    p3++;
	    p4++;
	}
	if (w)
	    printf("\n");
}

/* given a value, looks up the name */
static struct sym *
searchbyval(value, offset)
	unsigned value, *offset;
{
	struct sym lsym, *p = &lsym;
	register struct sym **pp, **ppend;

	*offset = 0;
	p->sym_value = value;
	pp = (struct sym **) bsearch((char *)&p, (char *)symtablepv, glb_symbols, sizeof (struct sym *), cmpsymbyval);
	if (pp == NULL) {
		/*
		 * Binary search for exact value failed, so try again with linear
		 * search to find value just before so result is "value+offset".
		 */
		for( pp = symtablepv, ppend = &symtablepv[glb_symbols]; pp != ppend; pp++) {
			if ( (*pp)->sym_value < value ) 
				continue;
			--pp;
			*offset = value - (*pp)->sym_value;
			return *pp;
		}
		return (struct sym *)NULL;
	}
	return *pp;
}

/* given a name, look up a value */
struct sym *
searchbynam(s)
	char *s;
{
	struct sym lsym, *p = &lsym, **pp;
	char buf[ MAXSTRLEN + 1 ];
	int count;

	buf[0] = '\0';
	p->sym_name = s;
	pp = (struct sym **) bsearch((char *)&p, (char *)symtablepn, glb_symbols, sizeof (struct sym *), cmpsymbynam);
	if (pp == NULL)  {	/* try '_'name */
		buf [ 0 ] = '_';
		strcpy(&buf[1], s);
		p->sym_name = buf;
		pp = (struct sym **) bsearch((char *)&p, (char *)symtablepn, glb_symbols, sizeof (struct sym *), cmpsymbynam);
	}

	if (pp == NULL)
		return (struct sym *) NULL;
	
	/* Got it -- check for duplicate names. */

	if (buf[0] == '\0') {
		count = look_dupname(s);
		if (count > 1)
			printf("Warning: %d entries for symbol '%s'\n", count, s);
	} else {
		count = look_dupname(buf);
		if (count > 1)
			printf("Warning: %d entries for symbol '%s'\n", count, buf);
	}

	return *pp;
}

/* given a name, return any sdb info */
struct sdb *
searchsdb(s)
	char *s;
{
	struct sdb lsdb, *p = &lsdb, **pp;
	int count;

	p->sdb_name = s;
	pp = (struct sdb **) bsearch((char *)&p, (char *)sdbtablepn, sdb_symbols, sizeof (struct sdb *), cmpsdbbynam);
	if (pp == NULL)
		return (struct sdb *)NULL;
	
	/* Got it -- check for duplicate names. */

	count = look_dupsdb(s);
	if (count > 1)
		printf("Warning: %d debug entries for symbol '%s'\n", count, s);

	return *pp;
}
	
/* Global interfaces */
char * 
lookbyval(addr, offsetp)
	unsigned addr, *offsetp;
{
	struct sym *p;

	if (addr < symtablepv[0]->sym_value || addr > symtablepv[glb_symbols-1]->sym_value)
		return NULL;
	p = searchbyval(addr, offsetp);
	if (p == NULL)
		return NULL;
	return p->sym_name;
}

unsigned int
lookbysym(symbol, value)
	char *symbol;
	unsigned *value;
{
	struct sym *p;
	struct sdb *pp;
	struct set_val *ppp;
	
	if (symbol == 0 || *symbol == '\0')
		return FALSE;
	*value = 0;
	memset(&sdbinfo,  0, sizeof sdbinfo);
	p = searchbynam(symbol);
	if ( p ) *value = p->sym_value;
	pp = searchsdb(symbol);
	if ( pp ) sdbinfo = *pp;
	ppp = searchsetval(symbol);
	if ( ppp ) {
		*value = ppp->sv_val.v_value;
		sdbinfo = ppp->sv_val.v_sdb;
	}
	if ( p || pp || ppp)
		return TRUE;
	return FALSE;
}

/* return the first descripter in *x */
/* see struct desc in crash.h */
desc(x)
	register short *x;
{
	register i;

	for (i = *x &~ 0xf; i; i <<= 2) {
		if ((i & 0xc000) == 0)
			continue;
		return  (i & 0xc000) >> 14;
	}
	return 0;
}

/* Misc COFF support routines follow. */
/* ********************************** */


/*
 * Quick check that file is in COFF format and that magic number
 * is correct.
 */

check_file_type()
{
	if (ISCOFF(HEADER(ldptr).f_magic)) {
	    if (HEADER(ldptr).f_magic != I386MAGIC)
		printf("Warning: bad magic number.  Will try to process\n");
	} else {
	    printf("Error: File is not in COFF format.\n");
	    exit(1);
	}
}

/*
 * Scan the sections and get section numbers for text, data, and bss.
 */

scan_sections()
{
	unsigned short sec,		/* current section */
		       nsec;		/* last section */
	SCNHDR	secthead;		/* section header structure */

	/* File last section number, give up if none. */

	nsec = HEADER(ldptr).f_nscns;
	if (nsec == 0) {
		printf("Error: section count is 0.\n");
		exit(1);
	}

	for (sec = 1; sec <= nsec; sec++) {
	    if (ldshread(ldptr, sec, &secthead) != SUCCESS) {
		printf("Error: ldshread fails for section %d\n", sec);
		exit(1);
	    }
	    if (streq(".text", secthead.s_name)){
		if (sect_text != 0) {
		    printf("Error: dupilcate text sections: %d, %d\n",
		      sect_text, sec);
		    exit(1);
		}
		sect_text = sec;
		text_size = secthead.s_size;
		text_offset = secthead.s_scnptr;
		text_start = secthead.s_vaddr;
		Etext = text_start + text_size;
	    } else if (streq(".data", secthead.s_name)) {
		if (sect_data != 0) {
		    printf("Error: dupilcate data sections: %d, %d\n",
		      sect_data, sec);
		    exit(1);
		}
		sect_data = sec;
		data_size = secthead.s_size;
		data_offset = secthead.s_scnptr;
		data_start = secthead.s_vaddr;
	    } else if (streq(".bss", secthead.s_name)) {
		if (sect_bss != 0) {
		    printf("Error: dupilcate bss sections: %d, %d\n",
		      sect_bss, sec);
		    exit(1);
		}
		sect_bss = sec;
		bss_size = secthead.s_size;
		bss_start = secthead.s_vaddr;
	    }
	}

	if (sect_text == 0 || sect_data == 0 || sect_bss == 0) {
		printf("Error: missed .text, .data, or .bss section\n");
		printf("       .text = %d\n", sect_text);
		printf("       .data = %d\n", sect_data);
		printf("       .bss = %d\n", sect_bss);
		exit(0);
	}

	if (debug[0])
	    printf("sections: .text = %d, .data = %d, .bss = %d\n",
	        sect_text, sect_data, sect_bss);
}



/*
 * routine for storing short string variables with little space wasted.
 * there is no way to free a string out of this, unless the entire
 * string pool block is freed.
 */

#define STRINGSIZE ((2048 - sizeof(struct Stringpool *)) / sizeof(char))

struct stringpool {
	char chars[STRINGSIZE];
	struct stringpool *prevpool;
};
typedef struct stringpool Stringpool;
static Stringpool *s_pool, *newpool;
static nextchar = STRINGSIZE + 1;		/* force first alloc */

char *stralloc(n)
	int n;
{
	if (nextchar + n > STRINGSIZE) {
		if (n > STRINGSIZE) {
			printf("stralloc: string too long!");
			printf("string size %d, max is %d\n", n, STRINGSIZE);
			exit(1);
		}
		newpool = (Stringpool *)malloc(sizeof(Stringpool));
		newpool->prevpool = s_pool;
		s_pool = newpool;
		nextchar = 0;
	}
	nextchar += n;
	return(&s_pool->chars[nextchar - n]);
}

char *save_name(sname)
	char *sname;
{
	register char *p, *q;
	register int len = 0;
	char *ret;

	p = sname;
	while (*p != '\0') {
		len++;
		p++;
	}
	ret = stralloc(len + 1);
	p = sname;
	q = ret;
	while (*p) {
		*q++ = *p++;
	}
	*q = '\0';

	return(ret);
}
