File:  [MW Coherent from dump] / coherent / b / conf / patch / patch.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:35 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

static char Copyright[] =	"$Copyright: (c) 1985, INETCO Systems, Ltd.$";
static char version[] =	"patch version 2.6 for COHERENT v.4.0";

/* (lgl-
 *	The information contained herein is a trade secret of Mark Williams
 *	Company, and  is confidential information.  It is provided  under a
 *	license agreement,  and may be  copied or disclosed  only under the
 *	terms of  that agreement.  Any  reproduction or disclosure  of this
 *	material without the express written authorization of Mark Williams
 *	Company or persuant to the license agreement is unlawful.
 *
 *	COHERENT Version 2.3.35
 *	Copyright (c) 1982, 1983, 1984.
 *	An unpublished work by Mark Williams Company, Chicago.
 *	All rights reserved.
 -lgl) */
/*
 * Patch binary system images
 * and possibly the running system.
 * This program is not expected to work other than on PC Coherent.
 * Certain hot patches may not be effective, since some values are only
 * referenced once at system initialization.
 *
 * $Log: patch.c,v $
 * Revision 1.1.1.1  2019/05/29 04:56:35  root
 * coherent
 *
 * Revision 1.6  92/11/25  15:32:04  bin
 * hal: update to read fom /dev/imem
 * 
 * Revision 1.5  92/07/06  15:41:09  bin
 * piggy: all hex numbers have leading zeroes to indicate their length
 * 
 * Revision 1.1	91/04/24  14:20:20 	bin
 * Initial revision
 * 
 * 87/02/01	Allan Cornish		/usr/src/cmd/conf/patch.c
 * myatol() routine added which recognizes numeric base specifications.
 * All references to atol() modified to use myatol().
 * main() now enables buffering on standard output.
 *
 */
char short_helpmessage[] = "\
patch -- alter COFF binary image\n\
Usage:	patch [ -v ][ -p ][ -k ] imagename symbol=value [ ... ]\n\
";

char helpmessage[] = "\
Options:\n\
	-v	Verbose mode--print what's being done.\n\
	-p	Peek only--do not write.\n\
	-k	Patch running system via /dev/kmem, /dev/kmemhi.\n\
	-K	Like -k but do not alter imagename.\n\
Patch alters the value of 'symbol' to 'value' in the binary 'imagename'.\n\
Both 'symbol' and 'value' may be composed of a decimal numeric constant\n\
or of a symbol in the image's symbol table, trailing '_' is significant,\n\
optionally offset by + or - a decimal numeric constant.\n\
The 'value' field may be optionally composed of 'makedev(d1, d2)' where 'd1'\n\
and 'd2' are decimal numbers and the result is a dev_t value.\n\
The size of the altered field is by default sizeof(int), but the 'value'\n\
specification may be followed by a ':' and a 'c', 's', 'i', or 'l' to\n\
explicitly specify a char, short, int, or long sized patch.\n\
\
";

#include <stdio.h>
#include <coff.h>
#include <canon.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "patch.h"

/*
 * Nlist tables and patch records.
 */
#define NNLS	512
int	nnls;	/* Number of nlist elements used */
SYMENT nl[NNLS*2];
int	sym_len = 0;	/* Number of bytes allocated for symbols.  */
char	*symbols=NULL;

PLIST pl[NNLS];

char *namep;		/* Name of object file to patch.  */
int nobin = 0;		/* Should we not patch the image?  */
int hotpatch = 0;	/* Are we patching /dev/kmem,/dev/kmemhi?  */
int verbose = 0;	/* Are we printing feedback?  */
int peek = 0;		/* Just peek--don't actually do the patch.  */

void main();
int getnames();
void badsym();
void getone();
void setfile();
void setkmem();
int patch();
long myatol();
void usage();
char *index();
char *realloc();


void
main(argc, argv)
	int argc;
	char *argv[];
{
	static char obuf[BUFSIZ];
	int c;			/* For reading options from getopt().  */
	int num_patches;	/* Number of patches to make on this file.  */

	extern int optind;
	extern char *optarg;

	/*
	 * Enable output buffering.
	 */
	setbuf( stdout, obuf );

	while ((c = getopt(argc, argv, "Kkpv?")) != EOF) {
		switch (c) {
		case 'K':
			hotpatch++;
			nobin++;
		case 'k':
			hotpatch++;
			break;
		case 'p':
			peek++;
			break;
		case 'v':
			verbose++;
			break;
		case '?':
			fprintf(stderr, "%s\n", version);
			usage(TRUE);	/* Does not return.  */
		default:
			usage(FALSE);	/* Does not return.  */
		}
	}

	/*
	 * There must be at least 2 arguments left.
	 */
	if (argc - optind < 2) {
		fprintf(stderr, "Missing arguments.\n");
		usage(FALSE);	/* Does not return */
	}
	
	namep = argv[optind++];	/* Fetch the name of the file to patch.  */

	num_patches = (argc - optind);
	if (getnames(num_patches, &(argv[optind])) == 0) {
		if (!nobin) {
			setfile(namep, num_patches, pl);
		}
		if (hotpatch) {
			setkmem(num_patches);
		}
		exit(0);
	}
	exit(1);
}

/*
 * Fill in the array of patch structures 'pl[]' based on the command line.
 * 'nn' is the number of symbol assignments; 'npp' is an argv of symbol
 * assignments.
 * Returns the number of invalid assignments.
 */
int
getnames(nn, npp)
	int nn;
	char **npp;
{
	register int i;
	register PLIST *p;
	register SYMENT *np;
	int nbad;

	nbad = 0;
	for (i = 0; i < nn; i += 1)
		if (i < NNLS-1)
			getone(i, npp[i]);

	/* Now we can look up all the symbols in the symbol table.  */
	coffnlist(namep, nl, symbols, nnls);

	for (i = 0; i < nn; i += 1)
		if (i >= NNLS)
			fprintf(stderr,
				"Too many patches: %s ignored\n", npp[i]);
		else {
			/* 'p' is the struct we fill in this time around.  */
			p = &pl[i];
			
			/* If the LHS was (part) symbolic, add in the value
			 * of the symbol.
			 */
			if ((np = p->p_lvnp) != NULL) {
				if (0xffff != np->n_type) {
					p->p_lval += np->n_value;
				} else {
					nbad += 1;
					badsym(np->n_offset);
				}
			}
			/* If the RHS was (part) symbolic, add in the value
			 * of the symbol.
			 */
			if ((np = p->p_rvnp) != NULL) {
				if (0xffff != np->n_type) {
					p->p_rval += np->n_value;
				} else {
					nbad += 1;
					badsym(np->n_offset);
				}
			}

			/* Fill in the value to be assigned.  */
			switch (p->p_type) {
			case 'c': p->p_val.p_char	= p->p_rval; break;
			case 's': p->p_val.p_short	= p->p_rval; break;
			case 'i': p->p_val.p_int	= p->p_rval; break;
			case 'l': p->p_val.p_long	= p->p_rval; break;
			default:
				nbad += 1;
				fprintf(stderr, "Bad data type %c in %s.\n",
					p->p_type, npp[i]);
				break;
			}
		}
	return (nbad);
}

void
badsym(offset)
	long offset;
{
	fprintf(stderr, "%s not found in %s\n",
		&(symbols[offset - sizeof(long)]), namep);
}

/*
 * Parse a symbolic assignment, filling in pl[i].
 */
void
getone(i, np)
	int i;			/* Which'th symbol assigment is this?  */
	register char *np;	/* The symbol assignment itself.  */
{
	register int n;
	register char *cp;
	char *nsym;		/* Temporary holder for 'symbols' realloc().  */
	long myatol();

	pl[i].p_lvnp = NULL;
	pl[i].p_lval = 0;
	pl[i].p_rvnp = NULL;
	pl[i].p_rval = 0;
	pl[i].p_type = 'i';

	/*
	 * If there is a type indicator, get it now.
	 */
	if (NULL != (cp = index(np, ':'))) {
		pl[i].p_type = cp[1];
	}

	/* Pull apart LHS of assignment.  */
	if (isalpha(*np) || *np == '_') {
		pl[i].p_lvnp = nl + nnls;	/* Allocate another SYMENT.  */
		/* Mark as not yet found.  */
		nl[nnls].n_type = 0xffff;
		/* Point at offset into 'symbols' for new name.  */
		nl[nnls].n_zeroes = 0;
		nl[nnls].n_offset = sizeof(long) + sym_len;

		/* Figure out how big the symbol is by looking for
		 * a non-alphanumeric or _ character.
		 */
		cp = np;
		for (n = 0; isalnum(*cp) || *cp == '_'; n += 1) {
			cp += 1;
		}
		/* Now allocate more space for symbol names.  */
		sym_len += n + sizeof('\0');
		if (NULL == (nsym = realloc(symbols, sym_len))) {
			/* This assignment is too long; skip it.  */
			sym_len -= n;
			fprintf(stderr,
				"Assignment too long; skipping:  %s\n", np);
			return;
		}
		symbols = nsym;	/* The realloc() worked.  */
		/* Copy the new symbol in place.  */
		cp = symbols + sym_len - (n + sizeof('\0'));
		strncpy(cp, np, n);
		cp[n] = '\0';

		nnls += 1;	/* Move up to next empty SYMENT.  */
		np += n;	/* Move on to next token.  */
	}
	/*
	 * If there is a '+' it has served its purpose by dropping us
	 * out of the for loop above.  Ignore it now.
	 */
	if (*np == '+')
		np += 1;

	/* Fetch a possible literal number.  */
	pl[i].p_lval = myatol(np);

	/* Pull apart RHS of assignment.  */
	np = index(np, '=');
	if (np != NULL) {
		np += 1;
		if (strncmp(np, "makedev(", 8) == 0) {
			/* RHS is a makedev() expression.  */
			int d1, d2;

			np = index(np, '(') + 1;
			d1 = myatol(np);
			np = index(np, ',');
			if (np != NULL) {
				d2 = myatol(np + 1);
				np = index(np, ')');
			} else
				d2 = 0;
			pl[i].p_rval = makedev(d1, d2);
			pl[i].p_type = 's';
			if (np == NULL)
				np = "";
			else
				np += 1;
			goto tail;
	} else if (isalpha(*np) || *np == '_') {
			/* The RHS must be a object symbol.  */
	
			pl[i].p_rvnp = nl + nnls;  /* Allocate another SYMENT.  */
			nl[nnls].n_type = 0xffff;  /* Mark as not yet found.  */

			/* Point at offset into 'symbols' for new name.  */
			nl[nnls].n_zeroes = 0;
			nl[nnls].n_offset = sizeof(long) + sym_len;
	
			/* Figure out how big the symbol is by looking for
			 * a non-alphanumeric or _ character.
			 */
			cp = np;
			for (n = 0; isalnum(*cp) || *cp == '_'; n += 1) {
				cp += 1;
			}
			/* Now allocate more space for symbol names.  */
			sym_len += n + sizeof('\0');
			if (NULL == (nsym = realloc(symbols, sym_len))) {
				/* This assignment is too long; skip it.  */
				sym_len -= n;
				fprintf(stderr,
				  "Assignment too long; skipping:  %s\n", np);
				return;
			}
			symbols = nsym;	/* The realloc() worked.  */
			/* Copy the new symbol in place.  */
			cp = &(symbols[sym_len - (n + sizeof('\0'))]);
			strncpy(cp, np, n);
			cp[n] = '\0';

			nnls += 1;	/* Move up to next empty SYMENT.  */
			np += n;	/* Move on to next token.  */
		}


		/*
		 * If there is a '+' is has served its purpose by dropping us
		 * out of the for loop above.  Ignore it now.
		 */
		if (*np == '+')
			np += 1;
		/* Fetch a possible literal number.  */
		pl[i].p_rval = myatol(np);
	}
tail:
	return;
}

/*
 * Modify the contents of /dev/kmem to match the array of patch
 * structures pl[].  The argument 'n' is the number of entries in pl[]
 * that should be processed.
 */
void
setkmem(n)
	int n;
{
	int fdlo, fdhi;
	register int i;
	char *symname;	/* Name of symbol in LHS being patched.  */

	/* Open up live memory for patching.  */
	if (peek) {
		if ((fdlo=open("/dev/kmem", O_RDONLY)) < 0) {
			fprintf(stderr, "Cannot open /dev/kmem for reading.\n");
			return;
		}
		if ((fdhi=open("/dev/kmemhi", O_RDONLY)) < 0) {
			fprintf(stderr, "Cannot open /dev/kmemhi for reading.\n");
			return;
		}
	} else {
		if ((fdlo=open("/dev/kmem", O_RDWR)) < 0) {
			fprintf(stderr, "Cannot open /dev/kmem.\n");
			return;
		}
		if ((fdhi=open("/dev/kmemhi", O_RDWR)) < 0) {
			fprintf(stderr, "Cannot open /dev/kmemhi.\n");
			return;
		}
	}

	/* Walk through pl[] blasting the new values into live memory.  */
	for (i = 0; i < n; i += 1) {
		int seekOffset = pl[i].p_lval;
		symname = &(symbols[pl[i].p_lvnp->n_offset - sizeof(long)]);

		if ((seekOffset & 0x80000000) == 0) {
			if(lseek(fdlo, seekOffset, 0) != -1L) {
				if (patch(fdlo, &pl[i], "/dev/kmem",
				  symname) < 0)
					fprintf(stderr,
					  "Write error in /dev/kmem\n");
			} else
				fprintf(stderr, "Seek error in /dev/kmem\n");
		} else {
			if(lseek(fdhi, seekOffset-0x80000000, 0) != -1L) {
				if (patch(fdhi, &pl[i], "/dev/kmemhi",
				  symname) < 0)
					fprintf(stderr,
					  "Write error in /dev/kmemhi\n");
			} else
				fprintf(stderr, "Seek error in /dev/kmemhi\n");
		}
	}
	close(fdlo);
	close(fdhi);
}


/*
 * Modify the file attached to descriptor 'fd' to match the single patch
 * structure 'p'.  The file descriptor should already be lseek()'d to
 * the correct place.
 * Returns 0 on success, -1 otherwise.  errno will be set on error.
 */
int
patch(fd, p, file, sym)
	int fd;
	PLIST *p;
	/* These two args are only for information.  */
	char *file;	/* Name of the file being patched.  */
	char *sym;	/* Name of the LHS symbol being patched.  */
{
	register char *bp;
	register int nc;
	union {
		char	p_char;
		short	p_short;
		int	p_int;
		long	p_long;
	} old_val;

	bp = &p->p_val;
	switch (p->p_type) {
	case 'c':	nc = sizeof(char);	break;
	case 's':	nc = sizeof(short);	break;
	case 'i':	nc = sizeof(int);	break;
	case 'l':	nc = sizeof(long);	break;
	}

	if (verbose || peek) {
	    old_val.p_long = 0;	/* Zero the whole buffer.  */

	    if (read(fd, &old_val, nc) != nc) {
	    	fprintf(stderr, "Can't read old value.\n");
	    } else {

		printf("%s: ", file);

		if (verbose) printf("old value of ");

		printf("%s: ", sym);
	    	switch (p->p_type) {
		case 'c':	printf("0x%02x", old_val.p_char);	break;
		case 's':	printf("0x%04x", old_val.p_short);	break;
		case 'i':	printf("0x%08x", old_val.p_int);	break;
		case 'l':	printf("0x%08x", old_val.p_long);	break;
		} /* switch */

		printf("\n");

		if (!peek) {	/* If only peeking, there is no new value.  */
			printf("%s: new value: ", file);
		    	switch (p->p_type) {
			case 'c':	printf("0x%02x", p->p_val.p_char);
					break;
			case 's':	printf("0x%04x", p->p_val.p_short);
					break;
			case 'i':	printf("0x%08x", p->p_val.p_int);
					break;
			case 'l':	printf("0x%08x", p->p_val.p_long);
					break;
			} /* switch */

			printf("\n");
		} /* if (verbose) */

		/* Go back for the write.  */
		lseek(fd, (long) (-nc), 1);
	    } /* if (read...) */

	} /* if (verbose || peek) */

	if (peek) {
		if (verbose) {
			printf("Just peeking, no write.\n");
		}
	} else if (write(fd, bp, nc) != nc) {
		return (-1);
	}
	return (0);
}

/**
 *
 * long
 * myatol( s )		-- Ascii to Long integer conversion.
 * char * s;
 *
 *	Input:	s = pointer to string containing a numeric prefix.
 *
 *	Action:	Parse input string.
 *		Parse optional leading sign character '-'.
 *		Parse optional numeric base specification '0', '0o', and '0x'.
 *		Parse following numeric digits.
 *
 *	Return:	Long integer value.
 *
 *	Notes:	Numeric parsing terminates on first non-digit.
 */
long
myatol( s )
	register char * s;
{
	register int base;
	register int sign;
	auto 	long valu;

	/*
	 * Check for leading negative sign.
	 */
	sign = 1;
	if ( *s == '-' ) {
		sign = -1;
		s++;
	}

	/*
	 * Check for base specification.
	 */
	base = 10;
	if ( *s == '0' ) {
		switch ( *++s ) {
		case 'x':	base = 16;	++s;	break;
		case 'o':	base =  8;	++s;	break;
		default:	base =  8;
		}
	}

	for ( valu = 0L; *s != '\0'; s++ ) {

		/*
		 * Decimal digit.
		 */
		if ( ('0' <= *s) && (*s <= '9') ) {
			valu *= base;
			valu += *s - '0';
		}

		/*
		 * Upper case hex digit.
		 */
		else if	( (base == 16) && ('A' <= *s) && (*s <= 'F') ) {
			valu *= base;
			valu += *s - ('A' - 10);
		}

		/*
		 * Lower case Hex digit.
		 */
		else if	( (base == 16) && ('a' <= *s) && (*s <= 'f') ) {
			valu *= base;
			valu += *s - ('a' - 10);
		}

		/*
		 * Not a digit.
		 */
		else
			break;
	}

	if ( sign < 0 )
		valu = -valu;

	return valu;
}

/*
 * Print out an usage message.
 */
void
usage(verbose)
	int verbose;
{
	fprintf(stderr, short_helpmessage);
	if (verbose) {
		fprintf(stderr, helpmessage);
	}
	exit(1);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.