

/*************************************************************************

                      Copyright (c) 1984 by Nick de Smith

        This software is supplied for interest and non-profit making
        purposes  only.   Under  no  circumstance  shall it be lent,
        copied or otherwise used for profit.  All  rights  regarding
        the  use  and  ownership of this software shall at all times
        remain with the author, who does not guarantee the  accuracy
        or  reliabilty  of this software and who will not accept any
        liability for its use.

        This software may not be copied or distributed  without  the
        inclusion of the above copyright notice.

        January 31st 1984

*************************************************************************/


/*************************************************************************


	Program :	STRIP.C

	Author	:	Nick de Smith		December 1982

	Description :

		Analyse a relocatable object file produced by either
		MAC.TSK or MACRO.SAV. Refer to TKB appendix A for an
		explanation of what is printed.
		This program will handle all knowen object record formats
		including complex relocation.


*************************************************************************/

#define	MAIN
#define	STRIP
#define	local	static

#include	<stdio.h>

FILE	*ip;				/* Input filename		*/

char	*version = "STRIP V1.4, Jan '84"; /* Version/id			*/

int	debug;				/* In debug mode		*/
int	concat;				/* Handle concatanated modules	*/

int	eom;				/* End of module flag		*/

int	rec_num;			/* Current record number	*/

char	buff[7];			/* Expanded rad 50 buffer	*/

char	wbuf[128];			/* Scratch working buffer	*/
char	*w_ptr;				/* Pointer into wbuf		*/

#define	MAX_RECLEN	512		/* Absolute max buffer length	*/

typedef struct buffer {

	int	b_next;			/* Pointer to next byte		*/
	int	b_left;			/* Number of bytes left		*/
	int	b_length;		/* Length of record		*/
	char	b_text[MAX_RECLEN];	/* Actual data			*/

} BUFFER, *BUFFER_PTR;

BUFFER		buff_1;			/* Input buffer			*/

BUFFER_PTR	b_ptr = &buff_1;	/* Pointer to ip buffer		*/


/*************************************************************************
*
*
*		o b j e c t     r e c o r d     t y p e s
*		-----------------------------------------
*
*
*************************************************************************/

#define	T_GSD		01		/* Global symbol directory	*/
#define	T_EGSD		02		/* End of GSD			*/
#define	T_TEXT		03		/* Text information		*/
#define	T_RELD		04		/* Relocation directory		*/
#define	T_ISD		05		/* Internal symbol directory	*/
#define	T_EOM		06		/* End of module		*/

char *rec_text[] = {
	"",
	"Declare global symbol directory",
	"End of global symbol directory",
	"Text information",
	"Relocation directory",
	"Internal symbol directory",
	"End of module"
	};


/*************************************************************************
*
*
*	    g l o b a l     s y m b o l     d i r e c t o r y
*	    -------------------------------------------------
*
*
*************************************************************************/

typedef struct {
	char	*bitclr;		/* Text if bit is clear		*/
	char	*bitset;		/* Text if bit is set		*/
} BIT_TEXT;

#define	T_MODN		000		/* Module name			*/
#define	T_CSECT		001		/* Control section name		*/
#define	T_ISN		002		/* Internal symbol name		*/
#define	T_TADDR		003		/* Transfer address		*/
#define	T_GSN		004		/* Global symbol name		*/
BIT_TEXT gsn_flags[] = {
	""	 	, "Weak "	,
	""		, ""		,
	""		, "Lib "	,
	"GS-Ref "	, "GS-Def "	,
	""		, ""		,
	"Abs "		, "Rel "	,
	""		, ""		,
	""		, ""
};
#define	T_PSECT		005		/* Program section name		*/
BIT_TEXT psect_flags[] = {
	""		, "Save "	,
	""		, "Lib "	,
	"Con "		, "Ovr "	,
	""		, ""		,
	"RW "		, "RO "		,
	"Abs "		, "Rel "	,
	"Loc "		, "Glo "	,
	"I "		, "D "
};
#define	T_IDENT		006		/* Program version id		*/
#define	T_MARR		007		/* Mapped array declaration	*/
#define	T_COMP		010		/* Completion routine address	*/

char *gsd_text[] = {
	"Module name",
	"Control section name",
	"Internal symbol name",
	"Transfer address",
	"Global symbol name",
	"Program section name",
	"Program version identification",
	"Mapped array declaration, Name",
	"Completion routine name"
	};


/*************************************************************************
*
*
*		r e l o c a t i o n      d i r e c t o r y
*		------------------------------------------
*
*
*************************************************************************/

#define	T_IREL		001		/* Internal relocation		*/
#define	T_GREL		002		/* Global relocation		*/
#define	T_IDREL		003		/* Internal displaced relocation*/
#define	T_GDREL		004		/* Global displaced relocation	*/
#define	T_GAREL		005		/* Global additive relocation	*/
#define	T_GADREL	006		/* Global additive disp. rel.	*/
#define	T_LDEF		007		/* Location counter definition	*/
#define	T_LMOD		010		/* Location counter modification*/
#define	T_LIMIT		011		/* .LIMIT directive		*/
#define	T_PREL		012		/* PSECT relocation		*/
#define	T_PDREL		014		/* PSECT displaced relocation	*/
#define	T_PAREL		015		/* PSECT additive relocation	*/
#define	T_PADREL	016		/* PSECT additive disp. rel.	*/
#define	T_CREL		017		/* Complex relocation (gosh!)	*/
#define	T_RLREL		020		/* Resident library relocation	*/
 
char *cmd_text[] = {
	"",
	"Internal relocation, value = #",
	"Global relocation, symbol = #",
	"Internal displaced relocation, value = ",
	"Global displaced relocation, symbol = ",
	"Global additive relocation, expr = #",
	"Global additive displaced relocation, expr = ",
	"Location counter definition, PC = ",
	"Location counter modification, . = ",
	"Program limits, .LIMIT",
	"PSECT relocation, PSECT = #",
	"",
	"PSECT displaced relocation, PSECT = ",
	"PSECT additive relocation, PSECT+value = #",
	"PSECT additive displaced relocation, PSECT+value = ",
	"Complex relocation, expr ...\n",
	"Reslib/Task base relocation, value = base + "
	};


/*************************************************************************
*
*
*		c o m p l e x     r e l o c a t i o n
*		-------------------------------------
*
*
*************************************************************************/

#define	C_NOP		000		/* NOP				*/
#define	C_PLUS		001		/* Binop plus			*/
#define	C_MINUS		002		/* Binop minus			*/
#define	C_MUL		003		/* Binop multiply		*/
#define	C_DIV		004		/* Binop divide			*/
#define	C_AND		005		/* Binop bitwise AND		*/
#define	C_OR		006		/* Binop bitwise OR		*/
#define	C_UMINUS	010		/* Unary op minus		*/
#define	C_UCOMP		011		/* Unary op complement		*/
#define	C_STORE		012		/* Store result			*/
#define	C_DSTORE	013		/* Store result (displaced)	*/
#define	C_FGLOB		016		/* Fetch global			*/
#define	C_FREL		017		/* Fetch relocatable value	*/
#define	C_FCONST	020		/* Fetch constant		*/
#define	C_FRB		021		/* Fetch reslib/task base	*/

char *comp_text[] = {
	"", "+", "-", "*", "/", "&", "!", "", "-", "^C" };

int   comp_prio[] = {
	 0,  1 ,  1 ,  1 ,  1 ,  1 ,  1 , 0 ,  5 ,   5  };

#define	MAX_C_LEN	128		/* Maximum length of one arg	*/

typedef struct c_elem {			/* CREL atom			*/

	int	c_prio;			/* Priority of this element	*/
	char	c_text[MAX_C_LEN];	/* Stacked text			*/

} C_ELEM;

#define	MAX_C_STACK	5		/* Depth of CREL eval stack	*/

C_ELEM	c_stack[MAX_C_STACK];		/* CREL evaluation stack	*/
int	c_sp;				/* Index of c_stack[]		*/



/*************************************************************************
*
*
*				m a i n
*				-------
*
*	Main program start point.
*
*************************************************************************/

main(argc, argv)
int	argc;
char	*argv[];
{
	register int i,c;
	register char *p;
	char *file;

	file = NULL;
	for (i = 1; i < argc; ++i) {
		p = argv[i];
		if (*p++ == '-') {
			while (c = *p++) {
				switch (isupper(c) ? tolower(c) : c) {

				case 'd':
					debug++;
					break;

				case 'c':
					concat++;
					break;

				default:
					usage();
				}
			}
		}
		else
			if (file)
				usage();
			else
				file = argv[i];
	}
	if (!file)
		usage();
	strip(file);
}


/*************************************************************************
*
*
*				s t r i p
*				---------
*
*	Analyse the internals of an object file.
*
*************************************************************************/
local
strip(file)
char *file;
{
	open_file(file);

	do_header(file);

	rec_num = 0;
	eom = FALSE;

	while (!eom) {

		read_record();

		process_record();

	};
}


/*************************************************************************
*
*
*			o p e n _ f i l e
*			-----------------
*
*	Open an object/stb file for read access.
*
*************************************************************************/
local
open_file(file)
char *file;
{
	if (!(ip = fopen(file,"ru")))
		error("Failed to open \"%s\"", file);
}

/*************************************************************************
*
*
*			d o _ h e a d e r
*			-----------------
*
*	Print the header for this run.
*
*************************************************************************/
local
do_header(file)
char *file;
{
	char *ctime();

	printf("Analysis of object file \"%s\" taken on %s\n",
		file, ctime(0));
	printf("Produced by %s\n", version);
}


/*************************************************************************
*
*
*			p r o c e s s _ r e c o r d
*			---------------------------
*
*	Process an object record.
*
*************************************************************************/
local
process_record()
{
	int	type;

	switch (type = next_word()) {

		case T_GSD:
		case T_EGSD:
		case T_TEXT:
		case T_RELD:
		case T_ISD:
		case T_EOM:
			printf("\n** %5d : %s\n", rec_num, rec_text[type]);
			break;
		default:
			bug("Bad object record, type = %o", type);
	}

	switch (type) {

		case T_GSD:
			dump_gsd();
			break;
		case T_TEXT:
			dump_text();
			break;
		case T_RELD:
			dump_reld();
			break;
		case T_EOM:
			if (!concat)
				eom = TRUE;
			break;
		default:
			break;
	}
}


/*************************************************************************
*
*
*			d u m p _ g s d
*			---------------
*
*	Dump a GSD type record.
*
*************************************************************************/
local
dump_gsd()
{
	int	flag, entry, value;
	char	*bit_list();

	while (b_ptr->b_left) {
		name_text();
		flag = next_byte();
		entry = next_byte();
		value = next_word();
		printf("   Entry : %s = %s", gsd_text[entry], buff);
		switch (entry) {

			case T_MODN:
			case T_IDENT:
				putchar('\n');
				break;

			case T_CSECT:
			case T_COMP:
				printf(", maximum length = %o\n",
						value);
				break;

			case T_ISN:
			case T_TADDR:
				printf(", offset = %06o\n", value);
				break;

			case T_GSN:
				printf(", value = %06o, flags = %s\n",
					value, bit_list(gsn_flags,flag));
				break;

			case T_MARR:
				printf(", value = %06o, flags = %3o\n",
						value, flag);
				break;

			case T_PSECT:
				printf(", maximum length = %06o, flags = %s\n",
					value, bit_list(psect_flags,flag));
				break;
		}
	}
}


/*************************************************************************
*
*
*			d u m p _ t e x t
*			-----------------
*
*	Dump a text record.
*
*************************************************************************/
local
dump_text()
{
	register int i;

	i = 0;
	printf("   Load address = %o\n", next_word());
	while (b_ptr->b_left) {
		i++;
		if (b_ptr->b_left > 1)
			printf("   %06o", next_word());
		else
			printf("   %03o", next_byte());
		if (!(i & 7))
			putchar('\n');
	}
	if (i & 7)
		putchar('\n');
}


/*************************************************************************
*
*
*			d u m p _ r e l d
*			-----------------
*
*	Dump a reloaction directory record.
*
*************************************************************************/
local
dump_reld()
{
	int	command, displacement;

	while (b_ptr->b_left) {
		command = next_byte();
		displacement = next_byte();
		printf("   Disp = %3o", displacement);
		if (command & 128) {
			printf(", (byte)");
			command &= 127;
		}
		printf(", Command = %s", cmd_text[command]);
		switch (command) {

			case T_IREL:
			case T_IDREL:
			case T_LMOD:
			case T_RLREL:
				printf("%o\n", next_word());
				break;

			case T_GREL:
			case T_GDREL:
			case T_PREL:
			case T_PDREL:
				name_text();
				printf("%s\n", buff);
				break;

			case T_GAREL:
			case T_GADREL:
			case T_LDEF:
			case T_PAREL:
			case T_PADREL:
				name_min();
				printf("%s+%o\n", buff, next_word());
				break;

			case T_LIMIT:
				putchar('\n');
				break;

			case T_CREL:
				eval_crel();
				break;

			default:
				bug("Bad relocation command, %o", command);
		}
	}
}


/*************************************************************************
*
*
*			e v a l _ c r e l
*			-----------------
*
*	Evaluate a complex relocation RLD entry.
*
*************************************************************************/
local
eval_crel()
{
	register int type, i;
	register char *temp;
	int j;

	c_sp = 0;

	while ((type = next_byte()) != C_STORE && type != C_DSTORE) {

		switch (type) {

			case C_NOP:
				if (debug)
					printf("   NOP\n");
				break;

			case C_PLUS:
			case C_MINUS:
			case C_MUL:
			case C_DIV:
			case C_AND:
			case C_OR:
				temp = cpystr(wbuf, c_stack[c_sp -= 2].c_text);
				temp = cpystr(temp, comp_text[type]);
				if (comp_prio[type] >= c_stack[++c_sp].c_prio)
					*temp++ = '<';
				temp = cpystr(temp, c_stack[c_sp].c_text);
				if (comp_prio[type] >= c_stack[c_sp--].c_prio)
					*temp++ = '>';
				*temp = '\0';
				cpystr(c_stack[c_sp].c_text, wbuf);
				c_stack[c_sp++].c_prio = comp_prio[type];
				if (debug)
					printf("   Binop '%s'\n", comp_text[type]);
				break;

			case C_UMINUS:
			case C_UCOMP:
				temp = cpystr(wbuf, comp_text[type]);
				if (comp_prio[type] > c_stack[--c_sp].c_prio)
					*temp++ = '<';
				temp = cpystr(temp, c_stack[c_sp].c_text);
				if (comp_prio[type] > c_stack[c_sp].c_prio)
					*temp++ = '>';
				*temp = '\0';
				cpystr(c_stack[c_sp].c_text, wbuf);
				c_stack[c_sp++].c_prio = comp_prio[type];
				if (debug)
					printf("   Unary op '%s'\n", comp_text[type]);
				break;

			case C_FGLOB:
				name_min();
				cpystr(c_stack[c_sp].c_text, buff);
				c_stack[c_sp++].c_prio = 10;
				if (debug)
					printf("   Fetch global %s\n", buff);
				break;

			case C_FREL:
				i = next_byte();
				sprintf(c_stack[c_sp].c_text, "{%03o:%o}",
					i, j = next_two_bytes());
				c_stack[c_sp++].c_prio = 10;
				if (debug)
					printf("   Fetch relocatable value, Seg %03o, offset %o\n",
					i, j);
				break;

			case C_FCONST:
				sprintf(c_stack[c_sp].c_text, "%o",
					i = next_two_bytes());
				c_stack[c_sp++].c_prio = 10;
				if (debug)
					printf("   Fetch constant %o\n", i);
				break;

			case C_FRB:
				sprintf(c_stack[c_sp].c_text, "%s",
					"{reslib/task base}");
				c_stack[c_sp++].c_prio = 10;
				if (debug)
					printf("   Fetch reslib/task base\n");
				break;

			default:
				bug("Bad complex reclocation entry, %o", type);
		}
	}

	if (c_sp != 1)
		printf("** Invalid RPN expression\n");
	for (i = 0; i < c_sp; i++)
		if (!strlen(c_stack[i].c_text))
			printf("     ** Null **\n");
		else
			printf("     %s\n", c_stack[i].c_text);
}


/*************************************************************************
*
*
*			b i t _ l i s t
*			---------------
*
*	Return a pointer to a buffer containing a list of bits of text.
*
*************************************************************************/
local char *
bit_list(bit_text, flags)
BIT_TEXT bit_text[];
register int flags;
{
	register int i;

	arg_i();
	for (i = 0; i <= 7 ; i++ )
		arg_s(flags & 1<<i ? bit_text[i].bitset : bit_text[i].bitclr);
	return(arg_n() ? wbuf : "<none>");
}

/*************************************************************************
*
*
*				a r g _ i
*				---------
*
*	Initialise the argument working buffer.
*
*************************************************************************/
local
arg_i()
{
	w_ptr = wbuf;
	*w_ptr = '\0';
}

/*************************************************************************
*
*
*				a r g _ n
*				---------
*
*	Return the number of characters in the argument buffer.
*
*************************************************************************/
local
arg_n()
{
	return(w_ptr - wbuf);
}

/*************************************************************************
*
*
*				a r g _ s
*				---------
*
*	Put a string into the argument working buffer.
*
*************************************************************************/
local
arg_s(t_ptr)
register char *t_ptr;
{
	while (*t_ptr)
		*w_ptr++ = *t_ptr++;
	*w_ptr = '\0';
}


/*************************************************************************
*
*
*			r e a d _ r e c o r d
*			---------------------
*
*	Read the next input record.
*
*************************************************************************/
local
read_record()
{
	b_ptr->b_left = b_ptr->b_length = fget(b_ptr->b_text, MAX_RECLEN, ip);
	b_ptr->b_next = 0;
	if (feof(ip))
		bug("End of input file");
	rec_num += 1;
}

/*************************************************************************
*
*
*			n e x t _ b y t e
*			-----------------
*
*	Get the next byte from the current input record.
*
*************************************************************************/
local
next_byte()
{
	b_ptr->b_left--;
	return(b_ptr->b_text[b_ptr->b_next++] & 0377);
}


/*************************************************************************
*
*
*			n e x t _ w  o r d
*			------------------
*
*	Get the next aligned word from the current input record.
*
*************************************************************************/
local
next_word()
{
	b_ptr->b_next += (b_ptr->b_next & 1);	/* Align to next word	*/
	b_ptr->b_left = b_ptr->b_length - b_ptr->b_next;
	return(next_two_bytes(b_ptr));
}

/*************************************************************************
*
*
*			n e x t _ t w o _ b y t e s
*	 		---------------------------
*
*	Get the next two bytes from the current input record as
*	a non-aligned word.
*
*************************************************************************/
local
next_two_bytes()
{
	register int temp;

	temp = next_byte();		/* Force order of evaluation	*/
	return ( temp | ( 0400 * next_byte() ) );
}


/*************************************************************************
*
*
*			n a m e _ t e x t
*			-----------------
*
*	Get an un-aligned name from the current input record.
*
*************************************************************************/
local
name_text()
{
	int	tbuff[2];
	tbuff[0] = next_two_bytes();
	tbuff[1] = next_two_bytes();
	r50toa(buff, tbuff, 2);
	return(buff[6] = 0);
}

/*************************************************************************
*
*
*			n a m e _ m i n
*			---------------
*
*	Get an un-aligned name from the current input record. Ignore
*	all trailing spaces.
*
*************************************************************************/
local
name_min()
{
	int	tbuff[2];
	tbuff[0] = next_two_bytes();
	tbuff[1] = next_two_bytes();
	radmin(tbuff);
}

/*************************************************************************
*
*
*			r a d m i n
*			-----------
*
*	Convert two radix 50 integers into ascii text. Trailing spaces
*	are removed.
*
*************************************************************************/
local
radmin(i_ptr)
register int *i_ptr;
{
	register char *t_ptr;

	r50toa(buff, i_ptr, 2);
	t_ptr = &buff[6];
	while (t_ptr != &buff[0] && *--t_ptr == '\040')
		;
	if (*t_ptr != '\040')
		t_ptr++;
	*t_ptr = '\0';
}


/*************************************************************************
*
*
*				u s a g e
*				---------
*
*	How to luse this utility.
*
*************************************************************************/
local
usage()
{
	error("Usage: strip -c file.ext");
}

/*************************************************************************
*
*
*				b u g
*				-----
*
*	Report a BUG (well.. a mis-feature actually!)
*
*************************************************************************/
local
bug(args)
{
	error("*STRIP* -F- at record %d, %r", rec_num, &args);
}
