[comp.os.minix] Kernel patcher.

Steinsbo%hsr.uninett@nac.no (Bjarne Steinsbo) (06/06/89)

Beware, here be Tygers !!

Here comes a dangerous program inspired by the fonts that Dale Schumacher
<dal@syntel.UUCP> posted a little while ago (good work !!). It was
primarely made to change fonts on the fly, but it should be general enough
to do other kinds of patching in the kernel. It makes use of the symbol table
that is embedded in any ACK-format output file.

The program uses the symbolic information in the .out file and a command file
written by the user that describes the patches to be done. The patches
may be applied directly to /dev/kmem or to the image file of the kernel.

To make a symbol file for the kernel, just remove the `rm -f kernel.out'
command in the makefile for the kernel.

I don't think this is the kind of program that should be made suid root :-)

Notes:
	o I have used some functions that are not part of Atari-Minix 1.1.
	  They have been posted with PC-Minix 1.3 & 1.4 though.
	o When patching the image file, remember the 512 (200 hex) byte
	  boot sector which is placed in the image file before the kernel
	  proper. Use an offset in the command file.
	o The program has been tested with both cc (souped-up library) and
	  gcc on the Atari. Some changes will need to be done to make it run
	  on a PC.
	o The out.h file was posted with the official upgrade of Atari-
	  Minix by Stevenson. It was also posted with mdb.
	o I have shamelessly stolen a lot of code from the mdb posting,
	  parts of this code was again taken from anm. Good code shouldn't
	  be wasted !!

Improvements, comments, ... are welcome.

Enjoy

Bjarne Steinbo		steinsbo@hsr.uninett or try
			steinsbo%hsr.uninett@norunix.bitnet

P.S. Do you have any more fonts, Dale? I especially liked the courier one !!

echo x - kpatch.c
sed '/^X/s///' > kpatch.c << '/'
X/* Program to patch the kernel while running Minix (ST).
X *
X *					Author : Bjarne Steinsbo
X *					   steinsbo@hsr.uninett or
X *					   steinsbo%hsr.uninett@norunix.bitnet
X *
X * usage : kpatch cmd_file [symbol_file [file_to_patch]]
X * 
X * The program reads commands from cmd_file, and patches file_to_patch
X * accordingly. The format of the cmd_file is :
X *
X * symbol {ws} no_bytes {ws} offset {ws} {patch-values | -filename}
X *
X * {ws} is spaces or tabs. All numerical arguments should be given in hex,
X * without any leading 0x.
X * Whether patch-values or filename is used is determined by the first
X * character. If it's a minus sign a file-name is assumed, a number signifies
X * patch-values. The used file-name will be without the leading -
X * The offset is the value that should be added to the address found in the
X * symbol table to get the correct address to patch.
X * A line starting with `#' signifies a comment.
X *
X * If no symbol_file is given, kpatch uses /usr/src/kernel/kernel.out
X * If no file_to_patch is given, kpatch uses /dev/kmem
X *
X * Two examples:
X *  1)	The command file is called `install.courier'
X#symbol		no_bytes	offset		filename
X_font16		800		0		-courier.fnt
X * The command `kpatch install.courier' will now patch the kernel table
X * describing the font, and install the (binary) contents of courier.fnt
X * instead.
X *  2)	The command file is called `new_1'
X_keynorm	1		2		30
X * The comand `kpatch new_1 /usr/src/gkernel/kernel.mix.sym' will patch
X * one byte in the kernel table describing keyboard code to ascii conversion
X * and make the `1' key return `0'. (No, I don't know why anyone should
X * want to do this :-)
X *
X * The program is not very forgiving if the command file contains anything
X * but valid commands that are correctly formatted, but then kernel-patching
X * will probably never be entirely safe !!
X *
X * P.S. If there could be any doubt, the program is of course entirely in
X * the public domain, but read the comments before the last section of the fil
e.
X */
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <ctype.h>
X#include <string.h>
X
X#include "out.h"
X
X#ifndef SMB_FILE
X#define SMB_FILE "/usr/src/kernel/kernel.out"
X#endif
X#ifndef KER_FILE
X#define KER_FILE "/dev/kmem"
X#endif
X
X#define DEBUG(x) /* x */
X
X#ifdef __STDC__
Xvoid	*malloc(unsigned);
Xvoid	*realloc(void *, unsigned);
Xchar	*bsearch(char *, char *, unsigned, unsigned, int(*)(char *, char *));
X#else
Xchar	*malloc();
Xchar	*realloc();
Xchar	*bsearch();
X#endif
X
Xint nsyms, npatch;
X
Xstruct outname *nbufp = NULL;
X
Xlong strtol();
Xchar *skip_ws();
X
Xstruct patch {
X  long pa_addr;
X  unsigned pa_nval;
X  unsigned char *pa_val;
X} *pbufp = NULL;
X
X
Xint main(argc,argv)
X  int argc;
X  char **argv;
X{
X  unsigned register i;
X  int fd;
X  char *s;
X
X  if (argc < 2 || argc > 4) usage();
X
X  if(!getsyms(argc < 3 ? SMB_FILE : argv[2])) exit(1);
X  if(!getpatch(argv[1])) exit(1);
X  s = argc > 3 ? argv[3] : KER_FILE;
X  fd = open(s,O_WRONLY);
X  if (fd == 0) {
X	fprintf(stderr,"kp: could not open file %s\n",s);
X	exit(1);
X  }
X
X  DEBUG(printf("Start patching >:-) !!\n"));
X  for(i=0;i<npatch;i++) {
X	struct patch *pp = &pbufp[i];
X	DEBUG(printf("Applying patch at addr = %ld\n",pp->pa_addr));
X	lseek(fd,pp->pa_addr,0);
X	write(fd,pp->pa_val,pp->pa_nval);
X  }
X  close(fd);
X  DEBUG(printf("Exiting ... \n"));
X  exit(0);
X}
X
X
Xint pcomp(s,t)
X  char *s;
X  struct outname *t;
X{
X  DEBUG(printf("Comparing %s to %s\n",s,t->on_mptr));
X  return(strcmp(s,t->on_mptr));
X}
X
X#define LIN_SIZE 1024
X#define BASE 16
X
Xint getpatch(file)
X  char *file;
X{
X  FILE *fi;
X  char linbuf[LIN_SIZE];
X  char *p;
X  char *s_start,*s_end;
X  int count;
X  long offset;
X
X  fi = fopen(file,"r");
X  if (fi == NULL) {
X	fprintf(stderr, "kp: cannot open %s\n", file);
X	return 0;
X  }
X
X  for(;;) {
X	if (fgets(linbuf,LIN_SIZE ,fi) == NULL) break;/* we are finished */
X	if (linbuf[0] == '#') continue;		/* skip comments */
X	s_start = skip_ws(linbuf);		/* skip white space */
X	*(s_end = strpbrk(s_start," \t")) = '\0';/* terminate symbol name */
X	DEBUG(printf("Symbol %s\n",s_start));
X	p = bsearch(s_start,nbufp,nsyms,(unsigned)sizeof(struct outname),pcomp);
X	if (p == NULL) {			/* symbol not found */
X	    fprintf(stderr,"kp: symbol not found %s\n",linbuf);
X	    fclose(fi);
X	    return 0;
X	} else {				/* OK, insert into p-buffer */
X	    if (pbufp == NULL)			/* allocate some space */
X		pbufp = (struct patch *)malloc((unsigned)sizeof (struct patch));
X	    else
X		pbufp = (struct patch *)realloc(pbufp,(unsigned)((npatch + 1)*sizeof (struct
 patch)));
X	    if (pbufp == NULL) {		/* allocation successfull ? */
X		fprintf(stderr,"kp: out of memory\n");
X		fclose(fi);
X		return 0;
X	    }
X	    s_start = skip_ws(s_end + 1);	/* get count and offset */
X	    count = (int)strtol(s_start,&s_end,BASE);
X	    s_start = skip_ws(s_end + 1);
X	    offset = strtol(s_start,&s_end,BASE);
X
X	    pbufp[npatch].pa_addr = ((struct outname *)p)->on_valu + offset;
X	    pbufp[npatch].pa_nval = count;
X	    pbufp[npatch].pa_val = (unsigned char *)malloc((unsigned)count);
X	    if (pbufp[npatch].pa_val == NULL) {	/* allocate space for data */
X		fprintf(stderr,"kp: out of memory\n");
X		fclose(fi);
X		return 0;
X	    }
X	    s_start = skip_ws(s_end + 1);	/* find start of data/file */
X	    if (*s_start == '-')  {	/* open and read the file */
X		int fd;
X		s_start++;
X		*(strpbrk(s_start," \t\n")) = '\0'; /* terminate file-name */
X		fd = open(s_start,O_RDONLY);
X		if (fd == 0) {
X		    fprintf(stderr, "kp: cannot open %s\n", s_start);
X		    fclose(fi);
X		    return 0;
X		}
X		read(fd,pbufp[npatch].pa_val,count); /* and read it */
X		close(fd);
X	    } else {			/* read values directly */
X		unsigned char *cp = pbufp[npatch].pa_val;
X		while(count--) {
X		     *cp++ = (unsigned char)strtol(s_start,&s_end,BASE);
X		     s_start = s_end + 1;
X		}
X	    }
X	    npatch++;
X	}
X  }
X  fclose(fi);
X  return 1;				/* successfull return */
X}
X
Xchar *skip_ws(s)
X  char *s;
X{
X  while(isspace(*s++))
X	;
X  return --s;
X}
X
Xusage() {
X  fprintf(stderr,"Usage: kpatch cmd_file [symbol_file [file_to_patch]]\n");
X  exit(2);
X}
X
X/******************END OF ORIGINAL CODE***********************/
X/* The following code is a slightly edited version of the last part
X * of mdbexp.c. strtol is also shamelessly stolen from the mdb posting.
X */
X
X/*
X * The following code is actually a highly edited
X * version of anm.c - The ACK version of nm
X * That file's header follows:
X */
X
X/* @(#)anm.c	1.6 */
X/*
X**	print symbol tables for
X**	ACK object files
X**
X**	anm [-gopruns] [name ...]
X*/
X
X#define	ushort	unsigned short
X
Xlong	off;
Xlong	s_base[S_MAX];	/* for specially encoded bases */
X
Xint getsyms(file)
X	char *file;
X{
X	int	compare();
X	FILE		*fi;
X	struct	outsect	sbuf;
X	struct	outhead	hbuf;
X	struct	outname	nbuf;
X	char		*cbufp;
X	long		fi_to_co;
X	long		n;
X	unsigned	readcount;
X	int		j;
X
X	fi = fopen(file,"r");
X	if (fi == NULL) {
X		fprintf(stderr, "kp: cannot open %s\n", file);
X		return 0;
X	}
X
X	getofmt((char *)&hbuf, SF_HEAD, fi);
X	if (BADMAGIC(hbuf)) {
X		fprintf(stderr, "kp: %s-- bad format\n", file);
X		fclose(fi);
X		return 0;
X	}
X
X	n = hbuf.oh_nname;
X	if (n == 0) {
X		fprintf(stderr, "kp: %s-- no name list\n", file);
X		fclose(fi);
X		return 0;
X	}
X
X	if (hbuf.oh_nchar == 0) {
X		fprintf(stderr, "kp: %s-- no names\n", file);
X		fclose(fi);
X		return 0;
X	}
X	if ((readcount = hbuf.oh_nchar) != hbuf.oh_nchar) {
X		fprintf(stderr, "kp: string area too big in %s\n", file);
X		exit(2);
X	}
X
X	/* store special section bases */
X	if (hbuf.oh_flags & HF_8086) {
X		int i;
X		for (i=0; i<hbuf.oh_nsect; i++) {
X			getofmt((char *)&sbuf, SF_SECT, fi);
X			s_base[i+S_MIN] =
X				(sbuf.os_base>>12) & 03777760;
X		}
X	}
X		 
X	if ((cbufp = (char *)malloc(readcount)) == NULL) {
X		fprintf(stderr, "kp: out of memory on %s\n", file);
X		exit(2);
X	}
X	fseek(fi, OFF_CHAR(hbuf), 0);
X	if (fread(cbufp, 1, readcount, fi) == 0) {
X		fprintf(stderr, "kp: read error on %s\n", file);
X		exit(2);
X	}
X	fi_to_co = (long)cbufp - OFF_CHAR(hbuf);
X
X	fseek(fi, OFF_NAME(hbuf), 0);
X	nsyms = 0;
X	while (--n >= 0) {
X		getofmt((char *)&nbuf, SF_NAME, fi);
X
X		if (nbuf.on_foff == 0)
X			continue; /* skip entries without names */
X
X		if ((nbuf.on_type&S_EXT)==0)
X			continue;
X
X		nbuf.on_mptr = (char *)(nbuf.on_foff + fi_to_co);
X
X		/* adjust value for specially encoded bases */
X		if (hbuf.oh_flags & HF_8086) {
X		    if (((nbuf.on_type&S_ETC) == 0) ||
X			((nbuf.on_type&S_ETC) == S_SCT)) {
X			j = nbuf.on_type&S_TYP;
X			if ((j>=S_MIN) && (j<=S_MAX))
X			    nbuf.on_valu += s_base[j];
X		    }
X		}
X
X		if (nbufp == NULL)
X			nbufp = (struct outname *)malloc(sizeof(struct outname));
X		else
X			nbufp = (struct outname *)realloc(nbufp, (nsyms+1)*sizeof(struct outname));
X		if (nbufp == NULL) {
X			fprintf(stderr, "db: out of memory on %s\n", file);
X			exit(2);
X		}
X		nbufp[nsyms++] = nbuf;
X	}
X
X	qsort(nbufp, nsyms, sizeof(struct outname), compare);
X
X	fclose(fi);
X	return 1;
X}
X
Xcompare(p1, p2)
Xstruct outname	*p1, *p2;
X{
X	return strcmp(p1->on_mptr,p2->on_mptr);
X}
X
Xgetofmt(p, s, f)
Xregister char	*p;
Xregister char	*s;
Xregister FILE	*f;
X{
X	register i;
X	register long l;
X
X	for (;;) {
X		switch (*s++) {
X/*		case '0': p++; continue; */
X		case '1':
X			*p++ = getc(f);
X			continue;
X		case '2':
X			i = getc(f);
X			i |= (getc(f) << 8);
X			*((short *)p) = i; p += sizeof(short);
X			continue;
X		case '4':
X			l = (long)getc(f);
X			l |= ((long)getc(f) << 8);
X			l |= ((long)getc(f) << 16);
X			l |= ((long)getc(f) << 24);
X			*((long *)p) = l; p += sizeof(long);
X			continue;
X		default:
X		case '\0':
X			break;
X		}
X		break;
X	}
X}
X
Xlong 
Xstrtol(s, p, base)
X	char	*s, **p;
X{
X	long	r;
X	int	sign, digit, hexlow, hexup, c;
X
X	r = 0L;
X	sign = 0;
X	while (*s == ' ' || *s == '\t') ++s;
X	if (*s == '-')
X	{
X		sign = 1;
X		++s;
X	}
X	if (base == 0)
X	{
X		if (*s == '0') base = (s[1] == 'x' || s[1] == 'X') ? 16 : 8;
X		else base = 10;
X	}
X	if (*s == '0' && (s[1] == 'x' || s[1] == 'X') && base == 16) s += 2;
X	digit = (base <= 10) ? (base + '0' - 1) : '9';
X	hexlow = 'a' + base - 10;
X	hexup = 'A' + base - 10;
X	for (c = *s ; ; c = *++s)
X	{
X		if (c >= '0' && c <= digit) c = c - '0';
X		else if (c >= 'a' && c < hexlow) c = c - 'a' + 10;
X		else if (c >= 'A' && c < hexup)	c = c - 'A' + 10;
X		else
X		{
X			if (p != 0) *p = s;
X			return sign ? -r : r;
X		}
X		r = r * base + c;
X	}
X}
/