[comp.sys.amiga] Lattice multiple definition checker

pej@cuuxb.UUCP (jatkowski) (12/19/86)

Trying to compile some programs under Lattice 3.10 that (unknown to me)
redefined routines in lc.lib prompted me to write this progam. The initial
comment describes what it does.
As far as I know, manx users will find this pretty useless because of
different formats for at least libraries.

					Paul Jatkowski
					{ihnp4!}cuuxb!pej

/*
** multidef.c - A program to scan a collection of .o and library files
**		and search for multiply defined symbols.
**		This can catch problems where a symbol is defined in your
**		routine as well as in a scanned library (for example you
**		have a routine "write" that calls printf....). The linker
**		will not catch this type of problem.
**
**		multidef [-r] [-d] files.....
**
**		the -r option will list all symbols referenced in the file.
**		the -d option will list all symbols defined in the file
**		the files can be any collection of unlinked .o and library
**			files. Wild card expansion is done using a Lattice
**			3.10 library routine.
**
**		In addition to any -r or -d output multidef will list any
**		symbols defined in more than one file.
**
**		This only works on .o files, linked executables have hunks
**		in them that this program doesn't understand, besides the
**		symbol information is gone from an executable anyway.
**
**	Copyright (c) 1886, Paul Jatkowski.
*/
#include <stdio.h>
#include <fcntl.h>

/* defines for the different hunk types */

#define HUNK_UNIT	0x3e7	/* start of a program unit */
#define HUNK_NAME	0x3e8	/* name of hunk (optional) */
#define HUNK_CODE	0x3e9	/* block of code, possibly to be relocated */
#define HUNK_DATA	0x3ea	/* block of initialized data, possibly relocated */
#define HUNK_BSS	0x3eb	/* uninitialized data */
#define HUNK_RELOC32	0x3ec	/* 32 bit relocation entries */
#define HUNK_RELOC16	0x3ed	/* 16 bit pc relative relocation entries */
#define HUNK_RELOC8	0x3ee   /*  8 bit pc relative relocation entries */
#define HUNK_EXT	0x3ef	/* external symbol info  ... see below */
#define HUNK_SYMBOL	0x3f0	/* symbol table info, optional, for debuggers */
#define HUNK_DEBUG	0x3f1	/* further debugging info */
#define HUNK_END	0x3f2	/* end of a hunk */
/* lattice 3.1 hunk types */
#define HUNK_DRELOC32	0x3f7	/* 32 bit data section relative relocation */
#define HUNK_DRELOC16	0x3f8	/* 16 bit data section relative relocation */
#define HUNK_DRELOC8	0x3f9	/*  8 bit data section relative relocation */

/* the following hunk defines are for load files */
#define HUNK_HEADER	0x3f3	/* info for loader ... lots of stuff in here */
#define HUNK_OVERLAY	0x3f5	/* overlay table for overlay programs */
#define HUNK_BREAK	0x3f6	/* end of overlay node */


/* defines for HUNK_EXT hunk
** These are the important ones for getting symbol define/reference info
*/

#define EXT_SYMB	0		/* symbol table */
#define EXT_DEF		1		/* relocatable definition */
#define EXT_ABS		2		/* absolute definition */
#define EXT_RES		3		/* resident library definition */
#define EXT_REF32	129		/* 32 bit reference to symbol */
#define EXT_COMMON	130		/* 32 bit reference to COMMON */
#define EXT_REF16	131		/* 16 bit reference to symbol */
#define EXT_REF8	132		/*  8 bit reference to symbol */
/* lattice 3.1 extra defines */
#define EXT_DREF32	133		/* 32 bit base relative to symbol */
#define EXT_DREF16	134		/* 16 bit base relative to symbol */
#define EXT_DREF8	135		/*  8 bit base relative to symbol */


#define	MAXBUF 100	/* buffer for "get_and_flush" and "myread" */
long	buf[MAXBUF];

/* operation flags */
int	pr_def = 0;	/* print symbol definition listing */
int	pr_ref = 0;	/* print symbol reference  listing */
 
/* defines for t_insert routine (binary tree) */
struct t_node {
	struct	tnode	*right;
	struct	tnode	*left;
	char	*key;		/* symbol name */
	char	*aux1;		/* file   name */
	char	*aux2;		/* module name */
};
typedef struct t_node T_node;

/* declarations for forward referenced routines */
T_node	*t_insert();
void	save_def();

void
myread(fd,ptr,sz)
int	fd,sz;
char	*ptr;
{
	if (read(fd,ptr,sz) != sz)
	{
		printf("short read: aborting\n");
		exit(10);
	}
}

/* read n LONGS from the object file, used to flush unnecessary sections.
** I suppose that a seek would work also...... */
void
get_and_flush(objfile,n)
int	objfile,n;
{
	int	n1;
	n *= 4;
	while (n > 0)
	{
		n1 = n > MAXBUF ? MAXBUF : n;
		n -= n1;
		myread(objfile,buf,n1);
	}
}
/*
** given a object or library file name, read it and find symbol entries.
** print references or definitions if the flags are set. Collect the
** to look for multiple defines
*/
void
procfile(fname)
char	*fname;
{
	int	moretodo = 1;	
	long	d1 = -1 ,d2;
	int	ext_type;
	char	curhunk[100];
	char	curname[100];
	int	hunkend = 0;
	int	objfile;

	if (pr_def || pr_ref)
		printf("\t=========== processing %s ============\n",fname);
		
	if ( (objfile = open(fname,O_RDONLY)) < 0)
	{
		perror("can't open input file");
		poserr("amiga error");
		return;
	}
	
	while (moretodo)
	{
		/* read what is hopefully a hunk type */
		if (read(objfile,(char *)&d1,sizeof(d1)) != sizeof(d1))
		{
			if (!hunkend)
				printf("Unexpected end of file! (no hunk end)\n");
			moretodo = 0;
			continue;
		}
		hunkend = 0;
		switch(d1)
		{
		case HUNK_UNIT:		/* 3e7  Start of program unit */
			myread(objfile,&d1,sizeof(d1));
			myread(objfile,buf,d1*4);
			buf[d1] = 0;
			strcpy(curhunk,(char *)buf);
#ifdef DEBUG
			printf("hunk unit: %s\n",curhunk);
#endif
			break;
								
		case HUNK_NAME:		/* 3e8  Name of a hunk */
			myread(objfile,&d1,sizeof(d1));
			myread(objfile,buf,d1*4);
			buf[d1] = 0;
			strcpy(curname,(char *)buf);
#ifdef DEBUG
			printf("hunk name: %s\n",curname);
#endif
			break;

		case HUNK_CODE:		/* 3e9  Code segment */
		case HUNK_DATA:		/* 3ea  Initialized Data segment */
			myread(objfile,&d1,sizeof(d1));
#ifdef DEBUG
			printf("code/data hunk length=%d\n",d1);
#endif	
			get_and_flush(objfile,d1);
			break;

		case HUNK_BSS:		/* 3eb  Unitialized Data segment */
			myread(objfile,&d1,sizeof(d1));
#ifdef DEBUG
			printf("bss section length=%d\n",d1);
#endif
			break;			

		case HUNK_RELOC32:	/* 3ec  32-bit relocation list */
		case HUNK_RELOC16:	/* 3ed  16-bit PC-relative relocation info */
		case HUNK_RELOC8:	/* 3ee  8-bit PC-relative relocation info */
		case HUNK_DRELOC32:
		case HUNK_DRELOC16:
		case HUNK_DRELOC8:
#ifdef DEBUG
			printf("reloc hunk type 0x%x\n",d1);
#endif
			myread(objfile,&d1,sizeof(d1));
			while (d1 != 0)
			{
				myread(objfile,&d2,sizeof(d2));
				get_and_flush(objfile,d1);
				myread(objfile,&d1,sizeof(d1));
			}
			break;
							
		case HUNK_EXT:		/* 3ef  External symbol info */
#ifdef DEBUG
			printf("hunk ext\n");
#endif
			myread(objfile,&d1,sizeof(d1));
 			while (d1 != 0)
			{
				ext_type = (d1 >> 24) & 0xff;
				d1 &= 0xffffff;
				switch(ext_type)
				{
				case EXT_SYMB:	/* symbol table */
					get_and_flush(objfile,d1+1);
					break;
				case EXT_DEF:	/* reloc def */
				case EXT_ABS:	/* abs def */
				case EXT_RES:	/* resident lib */
					myread(objfile,buf,d1*4);
					buf[d1] = 0;
					if (pr_def)
						printf("DEF %-45.45s: %s\n",(char *)buf,curhunk);
					save_def(fname,curhunk,(char *)buf);
					get_and_flush(objfile,1);
					break;
				case EXT_REF32:	/* 32 bit ref */
				case EXT_REF16:	/* 16 bit ref */
				case EXT_REF8:	/*  8 bit ref */
				case EXT_DREF32:
				case EXT_DREF16:
				case EXT_DREF8:
					myread(objfile,buf,d1*4);
					buf[d1] = 0;
					if (pr_ref)
						printf("REF %-45.45s: %s\n",(char *)buf,curhunk);

					myread(objfile,&d1,sizeof(d1));
					get_and_flush(objfile,d1);
					break;
				case EXT_COMMON:	/* 32 bit ref */
					myread(objfile,buf,d1*4);
					buf[d1] = 0;
					if (pr_def)
						printf("COM %-45.45s: %s\n",(char *)buf,curhunk);
					save_def(fname,curhunk,(char *)buf);

					myread(objfile,&d1,sizeof(d1));
					myread(objfile,&d1,sizeof(d1));
					get_and_flush(objfile,d1);
					break;
				default:
					printf("ERROR: unknown HUNK_EXT 0x%x\n",d1);
				}
				myread(objfile,&d1,sizeof(d1));
			}
			break;
			
		case HUNK_SYMBOL:	/* 3f0  Symbol table info */
			myread(objfile,&d1,sizeof(d1));
			while (d1 != 0)
			{
				get_and_flush(objfile,d1+1);
				myread(objfile,&d1,sizeof(d1));
			}
			break;
			
		case HUNK_DEBUG:	/* 3f1	Debug data */
			myread(objfile,&d1,sizeof(d1));
			get_and_flush(objfile,d1);
			break;
			
		case HUNK_END:		/* 3f2  End of this hunk */
			hunkend++;
			break;
		case HUNK_HEADER:	/* 3f3  hunk summary info for loader */
		case HUNK_OVERLAY:	/* 3f5  overlay table info */
		case HUNK_BREAK:	/* 3f6  end of overlay node */
		default:
			printf("ERROR: can't handle hunk 0x%x\n",d1);
		}
		
	}
	close(objfile);
}

#define MAXEXP 100

void
main(argc,argv)
int	argc;
char	*argv[];
{
	int	i,j;
	int	matches;
	char	names[3000];	/* for expanded file names */
	char	*namep[MAXEXP];	/* array for pointers to file names */
	
	/* parse args, set global flags */

	if ( argc < 2)
	{
		printf("usage: %s [-d] [-r] objfile1 ojbfile2 ...\n",argv[0]);
		exit(1);
	}
	/*
	** Arg processing, nothing great, but sufficient.	
	*/
	for (i = 1 ; i < argc ; i++)
	{
		if (strcmp(argv[i],"-r") == 0)
		{
			pr_ref = 1;
			continue;
		}
		if (strcmp(argv[i],"-d") == 0)
		{
			pr_def = 1;
			continue;
		}
		/*
		** not an arg, must be an object file. 
		** Lattice 3.10 provides the getfnl routine that will take
		** an amigados pattern and build a list of matching files.
		** It this is not available, the input file name could be
		** passed directly to procfile, but the wild card expansion
		** is nice.
		*/
		matches = getfnl(argv[i],names,sizeof(names),0);
		if (matches > 0)
		{
			/* set up pointer array to point to each string */
			if (strbpl(namep,MAXEXP,names) != matches)
			{
				printf("%s - expansion overflow\n",argv[i]);
				continue;
			}
			/* sort the pointers, just to be nice */
			strsrt(namep,matches);
			
			/* process the files .... */
			for ( j = 0 ; j < matches ; j++)
				procfile(namep[j]);
		}
		else
		{
			printf("expansion failed for %s\n",argv[i]);
		}
	}
}

/*
** build a tree node and call t_insert to put it into the tree of defs.
** if t_insert returns  0, this is the only definition of the symbol.
** if t_insert returns -1, there was some unexpected error in t_insert.
** if t_insert returns anything else, it's a pointer to a node containing
**	the first definiton of the symbol. The current entry is not saved
**	in the tree.
*/
void
save_def(fname,hunk,sym)
char	*fname, *hunk, *sym;
{
	/* this routine does the work of building an internal symbol table */
	static T_node t1;
	static T_node *tree = (T_node *)0;
	T_node *rval;
	
	t1.key = sym;
	t1.aux1 = fname;
	t1.aux2 = hunk;
	t1.right = (T_node *)0;
	t1.left  = (T_node *)0;
	rval = t_insert(&tree,&t1);
	if ((int) rval != 0 && (int)rval != -1)
	{
		printf("%s defined in %s(%s) and %s(%s)\n",
			sym,rval->aux1,rval->aux2,fname,hunk);
	}
}

/*
** Insert a node into a binary tree. The tree built is never walked so only
** the insert routine is needed
*/
T_node *
t_insert(head,node)
T_node	**head;  /* note double indirection, this simplfies the code later */
T_node	*node;
{
	T_node  *newnode;
	static char *O_aux1 = (char *)0;
	static char *O_aux2 = (char *)0;
	int	result;
#ifdef DEBUG
	printf("key=%s aux1=%s aux2=%s\n",node->key,node->aux1,node->aux2);	
#endif
	/* check for null head pointer, if so create first node */
	if (*head == (T_node *)0)
	{
		newnode = (T_node *)malloc(sizeof(T_node));
		if (newnode == (T_node *)0)
			return((T_node *)-1);

		newnode->key =
			 (char *) malloc(strlen(node->key)+1);
		strcpy(newnode->key,node->key);
		
		newnode->aux1 = O_aux1 = (char *)malloc(strlen(node->aux1)+1);
		strcpy(newnode->aux1,node->aux1);

		newnode->aux2 = O_aux2 = (char *)malloc(strlen(node->aux2)+1);
		strcpy(newnode->aux2,node->aux2);
#ifdef DEBUG
		printf ("newnode =0x%x\n",newnode);
#endif
		newnode->right = (T_node *)0;
		newnode->left  = (T_node *)0;
		*head = newnode;
		return(0);
	}
	/* walk the tree to find a good spot */
	while (*head != (T_node *)0)
	{
#ifdef DEBUG
		printf ("TOL: head=0x%x *head=0x%x compare <%s> <%s>\n",
			head,*head,(*head)->key,node->key);
#endif
		if ( (result = strcmp((*head)->key,node->key)) == 0)
		{
			return(*head);	/* return pointer to dup node */
		}
#ifdef DEBUG
		printf ("result=%d\n",result);
#endif
		if (result >0)
			head = (T_node **)&(*head)->right;
		else
			head = (T_node **)&(*head)->left;
	}
#ifdef DEBUG
	printf ("EOL: head=0x%x *head=0x%x\n",head,*head);
#endif
	/* didn't find it, link it in....
	** only allocate new space for file and module name if different than
	** last one remembered, otherwise use the previous string */
	if ( (*head = (T_node *)malloc(sizeof(T_node))) == (T_node *)0)
	{
		printf("can't allocate space for node!\n");
		return((T_node *)0);
	}
	(*head)->key = (char *)malloc(strlen(node->key)+1);
	strcpy((*head)->key,node->key);
	
	if (strcmp(node->aux1,O_aux1) == 0)
		(*head)->aux1 = O_aux1;		/* same thing! */
	else
	{
		(*head)->aux1 = (char *)malloc(strlen(node->aux1)+1);
		strcpy((*head)->aux1,node->aux1);
	}
	if (strcmp(node->aux2,O_aux2) == 0)
		(*head)->aux2 = O_aux2;		/* same thing! */
	else
	{
		(*head)->aux2 = (char *)malloc(strlen(node->aux2)+1);
		strcpy((*head)->aux2,node->aux2);
	}
	(*head)->right = (T_node *)0;
	(*head)->left  = (T_node *)0;
	return((T_node *)0);
}