ftp.nice.ch/pub/next/developer/objc/mach/dis.N.bs.tar.gz#/symtab.c

This is symtab.c in view mode; [Download] [Up]

/*	symtab.c -- Sorted symbol table management for disassembler
 *  
 *	Copyright (C) 1989 by Bill Spitzak.
 *	See Copyright notice in makefile
 *  
 *	All symbols are stored in the Mach-O format "symbol"
 *	structure, and the code calling this allocates those structures,
 *	this just manipulates pointers to them.  This is because most
 *	of the symbols are in the source file and thus already allocated
 *	when mapped.
 *  
 *	There are two types of symbols, those with addresses, and those
 *	without (no address is indicated by a section number of 0).  These
 *	non-address symbols are assumed to be "bound" to the address symbol
 *	before them in the table.  Since there can be any number of them
 *	after a symbol, and they themselves don't contain the address, this
 *	makes the sorting difficult.
 *  
 *	Since "absolute" symbols are used to mark the mapped locations of
 *	shared libraries and otherwise used for symbols, they are forced to
 *	have a section number by the file reader, so they are sorted.
 */

#include "dis.h"

/* $Log:	symtab.c,v $
 * Revision 1.5  94/08/22  22:32:07  ediger
 * ditch movmem macro
 * 
 * Revision 1.4  94/05/30  23:06:38  ediger
 * some "readability" corrections
 * 
 * Revision 1.3  94/01/19  00:12:08  ediger
 * bug fixes, reformatting
 * 
 * Revision 1.2  94/01/16  17:17:50  ediger
 * ditched phony nlist struct def
 * 
 * Revision 1.1  94/01/16  16:48:35  ediger
 * Initial revision
 *  */

static char rcsident[] = "$Id: symtab.c,v 1.5 94/08/22 22:32:07 ediger Exp Locker: ediger $";

/*	Symbols are sorted with a linked list of these pointer tables.
 *	Since symbols can be inserted into the middle of a list, a table
 *	may be split into two so there are blank entries for the new
 *	symbol to be added.
 */
#define TABLE_SIZE 251		/* note picked to get efficient malloc size */

struct symtab
{
	struct symtab *prev;
	struct symtab *next;
	int            size;
	int            address;	/* address of first symbol in table */
	struct nlist  *table[TABLE_SIZE];
};

struct symtab initialsymtab;

/*	To make normal symbol operations fast, there is a "current symbol".
 *	This is always a real symbol, either the one returned by the last
 *	call, or if that returned zero, the symbol before the position it
 *	should be in the table (so nextsymbol will return an address after
 *	the symbol).
 *	If a non-found symbol is before the first one, it sets an offset of
 *	-1 into the first table.  This is the only time it is not the address
 *	of a real symbol.
 */

static struct symtab *symtab = &initialsymtab;	/* which table */
static int            symoffset = -1; /* offset into table */
int                   symaddress;     /* cached address, for NO_SECT symbols */

struct nlist *
currentsymbol(void)
{
	if (symoffset < 0)
		return (0);
	return (symtab->table[symoffset]);
}

/*	The current symbol may be pushed/popped, so the location is not
 *	lost if you use the symbol table in a subroutine.
 *	Don't forget to fix these stacked references if the tables are
 *	modified!
 */
#define STACKSIZE 10
static struct symtab *symtabstack[STACKSIZE];
static int offsetstack[STACKSIZE];
static int addressstack[STACKSIZE];
static int stackptr;

void 
pushsymbol(void)
{
	if (stackptr >= STACKSIZE)
	{
		printf("Symbol stack overflow.\n");
		exit(1);
	}

	symtabstack[stackptr] = symtab;
	offsetstack[stackptr] = symoffset;
	addressstack[stackptr] = symaddress;
	stackptr++;
}

void 
popsymbol(void)
{
	if (!stackptr)
	{
		printf("Symbol stack underflow.\n");
		exit(1);
	}

	stackptr--;
	symtab     = symtabstack[stackptr];
	symoffset  = offsetstack[stackptr];
	symaddress = addressstack[stackptr];
}

/*	All table lookups are based on this procedure, which returns a
 *	pointer to the last symbol, including NO_SECT (no address) ones
 *	before or equal to a certain address.  This is thus a pointer
 *	to the symbol before where a new symbol at a certain address
 *	should be added.
 */
static struct nlist *
lastsymbol(unsigned address)
{
	struct symtab *t;
	int     i;
	unsigned a;

	t = symtab;
	i = symoffset;
	a = symaddress;

	while (t->next && t->next->address <= address)
	{
		t = t->next;
		i = 0;
		a = t->address;
	}

	if (a > address)
	{
		i = 0;
		while ((a = t->address) > address)
		{
			if (!t->prev)
			{
				i = -1;
				break;
			}
			t = t->prev;
		}
	}

	/* gah! linear search: */
	while (++i < t->size)
	{
		if (t->table[i]->n_sect)
		{
			unsigned b = t->table[i]->n_value;
			if (b > address)
				break;
			a = b;
		}
	}

	i--;
	symtab = t;
	symoffset = i;
	symaddress = a;

	return (i >= 0 ? t->table[i] : 0);
}

/*	Find the first symbol at or after an address.  Returns 0 only if
	the address is after the last symbol.  This is used by disassembly
	printout, to find what symbols need to be printed at a certain
	point.
*/
struct nlist *
firstsymbol(unsigned address)
{
	if (address == 0)
	{
		while (symtab->prev)
			symtab = symtab->prev;

		symaddress = 0;
		symoffset = -1;

	} else
		lastsymbol(address - 1);

	return (nextsymbol());
}

/*	Find the last label for an address.  This only returns N_SECT symbols
	which will print as labels.  It returns the last one if there are
	several (this is usually correct).  If there is none, returns 0 (and
	nextsymbol will return the same thing firstsymbol would).
*/
struct nlist *
findlabel(unsigned address)
{
	struct nlist *n;
	n = lastsymbol(address);
	if (symaddress != address)
		return (0);

	if (n != NULL)
	{
		while (n->n_sect == NO_SECT || (n->n_type & N_STAB))
		{
			if (symoffset)
				symoffset--;
			else if (!symtab->prev)
				return (0);
			else
			{
				symtab = symtab->prev;
				symoffset = symtab->size - 1;
			}

			n = symtab->table[symoffset];

			if (n->n_sect)
			{
				symaddress = n->n_value;
				if (symaddress < address)
					return (0);
			}
		}
	}

	return (n);
}

/*	Advance to the next symbol and return it.  Return 0 if no more: */
struct nlist *
nextsymbol(void)
{
	struct nlist *n;

	if (symoffset < symtab->size - 1)
		n = symtab->table[++symoffset];
	else if (!symtab->next)
		return (0);
	else {
		symtab = symtab->next;
		symoffset = 0;
		n = symtab->table[0];
	}

	if (n->n_sect)
		symaddress = n->n_value;

	return (n);
}

/*	Add a new symbol.  If it is a NO_SECT symbol, it is inserted after
 *	the current one.  If it has an address, it is added after all other
 *	symbols at that address.
*/
void 
addsymbol(struct nlist *new)
{
	struct symtab *t;
	int     i;

	if (new->n_sect)
	{
		lastsymbol(new->n_value);
		symaddress = new->n_value;
	}

	if (!++symoffset)
		symtab->address = symaddress;

	if (symtab->size < TABLE_SIZE)
	{
		memmove(
			symtab->table + symoffset + 1,
			symtab->table + symoffset,
			(symtab->size - symoffset) * sizeof(struct nlist *)
		);
		symtab->size++;
		for (i = 0; i < stackptr; i++)
		{
			if (symtabstack[i] == symtab && offsetstack[i] >= symoffset)
				offsetstack[i]++;
		}

	} else {

		t = malloc(sizeof(struct symtab));
		t->prev = symtab;
		t->next = symtab->next;
		if (t->next)
			t->next->prev = t;
		symtab->next = t;

		if (symoffset >= TABLE_SIZE)
		{
			symtab = t;
			t->size = 1;
			t->address = symaddress;
			symoffset = 0;
		} else {

			t->size = TABLE_SIZE - symoffset;
			t->address = symtab->table[symoffset]->n_value;

			memmove(
				t->table,
				symtab->table + symoffset,
				(t->size) * sizeof(struct nlist *)
			);

			symtab->size = symoffset + 1;

			for (i = 0; i < stackptr; i++)
			{
				if (symtabstack[i] == symtab && offsetstack[i] >= symoffset)
				{
					symtabstack[i] = t;
					offsetstack[i] -= symoffset;
				}
			}
		}
	}
	symtab->table[symoffset] = new;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.