/*	demolish 1.1 - Take an image to pieces.		Author: Kees J. Bot
 */
#define nil 0
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <a.out.h>
#include <blocksize.h>
#include "image.h"

/* Either an image made by installboot, or a standard 1.5 or 1.6.* image made
 * by build can be taken apart.  The latter is a bit tricky.
 */

#define SECTOR_SIZE	512

void report(label) char *label;
/* demolish: label: Permission denied */
{
	int e= errno;
	fprintf(stderr, "demolish: ");
	errno= e;
	perror(label);
}

void fatal(label) char *label;
{
	report(label);
	exit(1);
}

void bread(f, name, buf, len) FILE *f; char *name, *buf; size_t len;
/* Read len bytes.  Don't dare return without them. */
{
	if (fread(buf, (int) len, 1, f) != 1) {
		if (ferror(f)) fatal(name);
		fprintf(stderr, "demolish: Unsuspected EOF on %s\n", name);
		exit(1);
	}
}

void bwrite(f, name, buf, len) FILE *f; char *name, *buf; size_t len;
{
	if (fwrite(buf, (int) len, 1, f) != 1) fatal(name);
}

void position(f, name, pos, key) FILE *f; char *name; off_t pos; int key;
{
	if (fseek(f, pos, key) != 0) fatal(name);
}

void banner()
{
	printf("    text    data     bss     size\n");
}

void pr_sizes(hdr, proc) struct exec *hdr; char *proc;
{
	printf("%8ld%8ld%8ld%9ld  %s\n",
		hdr->a_text, hdr->a_data, hdr->a_bss,
		hdr->a_text + hdr->a_data + hdr->a_bss, proc
	);
}

void copyexec(imagef, image, procf, proc, count, alen)
	FILE *imagef; char *image;
	FILE *procf; char *proc;
	long count; off_t *alen;
/* Copy a segment of an executable.  It is padded to a sector in image. */
{
	char buf[SECTOR_SIZE];

	while (count > 0) {
		bread(imagef, image, buf, sizeof(buf));
		*alen-= sizeof(buf);

		bwrite(procf, proc, buf,
			count < sizeof(buf) ? (size_t) count : sizeof(buf));
		count-= sizeof(buf);
	}
}

void dem_newimage(image) char *image;
/* Take an installboot created image apart to executables.  Unlike the build
 * created image, this is straightforward.
 */
{
	FILE *imagef, *procf;
	off_t len;
	struct stat st;
	struct image_header hdr;
	char buf[SECTOR_SIZE];

	if (stat(image, &st) < 0) fatal(image);

	/* Without the size of the image, it's length is undetermined. */
	if (!S_ISREG(st.st_mode)) {
		fprintf(stderr, "demolish: %s is not a file, expect an error\n",
			image);
		len= -1;
	} else
		len= st.st_size;

	if ((imagef= fopen(image, "r")) == nil) fatal(image);

	banner();

	while (len != 0) {
		/* Extract a program, first sector is an extended header. */
		bread(imagef, image, buf, sizeof(buf));
		len-= sizeof(buf);

		memcpy((void *) &hdr, (void *) buf, sizeof(hdr));

		if (BADMAG(hdr.process)) {
			fprintf(stderr, "demolish: %s contains a bad header\n",
				image);
			exit(1);
		}
		if ((procf= fopen(hdr.name, "w")) == nil) fatal(hdr.name);

		pr_sizes(&hdr.process, hdr.name);

#ifdef A_PAL
		if (hdr.process.a_flags & A_PAL) {
			/* A page aligned process contains a header in text. */
			hdr.process.a_text+= hdr.process.a_hdrlen;
		} else
#endif
			bwrite(procf, hdr.name, (char *) &hdr.process,
						(size_t) hdr.process.a_hdrlen);

		/* Extract text and data segments. */
		if (hdr.process.a_flags & A_SEP) {
			copyexec(imagef, image, procf, hdr.name,
						hdr.process.a_text, &len);
			copyexec(imagef, image, procf, hdr.name,
						hdr.process.a_data, &len);
		} else {
			copyexec(imagef, image, procf, hdr.name,
				hdr.process.a_text + hdr.process.a_data, &len);
		}

		/* Symbol table (if any). */
		copyexec(imagef, image, procf, hdr.name,
						hdr.process.a_syms, &len);

		if (fclose(procf) == EOF) fatal(hdr.name);
	}
}

/* Demolishing something is easy, except when you want to be able to put it
 * together again.  Build patches just enough data into the image to allow
 * it to be taken apart.  Finding the sizes array at the start of kernel data
 * is simple for sep I&D, tricky for common I&D.  The data segment of the other
 * programs is found by looking for the magic number.  Only if fs is common I&D
 * *and* contains the magic number in its text segment will this go wrong.  We
 * need fs data too so that the start and size of init can be patched into it
 * later.
 */
#define CLICK_SHIFT	8	/* Assumed click shift of a "build" image. */
#define CLICK_SIZE	(1 << CLICK_SHIFT)
#define HCLICK_SHIFT	4
#define HCLICK_SIZE	(1 << HCLICK_SHIFT)

#define MAGIC_OFF	0	/* Offset of magic # in data seg. */
#define CLICK_OFF	2	/* Offset of click_shift in data seg. */
#define KCLICK_OFF	2	/* Offset in kernel text to click_shift. */
#define FLAGS_OFF	4	/* Offset in kernel text to flags. */
#define KERNEL_D_MAGIC	0x526F	/* In kernel data. */
#define OTHER_D_MAGIC	0xDADA	/* The other processes. */

/* Offsets of sizes patched into kernel and fs. */
#define P_SIZ_OFF	0	/* Process' sizes into kernel data. */
#define P_KDS_OFF	4	/* Kernel ds into kernel text. */
#define P_INIT_OFF	4	/* Init cs & sizes into fs data. */
#define P_SYMS_OFF	2	/* Symbol table offset into text. */

#define MINIXSEG	0x0060	/* Minix is loaded here (rounded up towards
				 * the click size).
				 */
/* This address may be used to locate the data segment of a common I&D kernel:
 * (Using the mov sp,#k_stktop instruction.)
 */
#define K_STKTOP	0x0014	/* Offset of this address in kernel text. */
#define DTA2TOP		528	/* Go back this much to find kernel data. */

#define PROCESS_MAX	    16

/* The data segments of each process must start with: */
char kernel_d_magic[2] = {
	KERNEL_D_MAGIC & 0xFF, KERNEL_D_MAGIC >> 8
};
char other_d_magic[4] = {
	OTHER_D_MAGIC & 0xFF, OTHER_D_MAGIC >> 8, CLICK_SHIFT, 0
};

void extr_text(imagef, image, procf, proc, hdr)
	FILE *imagef; char *image;
	FILE *procf; char *proc;
	struct exec *hdr;
/* Extract the text segment by simply copying a_text bytes (slowly). */
{
	unsigned long n= hdr->a_text;
	char c;

	while (n > 0) {
		bread(imagef, image, &c, sizeof(c));
		bwrite(procf, proc, &c, sizeof(c));
		n--;
	}
}

void extr_data(imagef, image, procf, proc, hdr)
	FILE *imagef; char *image;
	FILE *procf; char *proc;
	struct exec *hdr;
/* Extract the data segment.  Look for the magic number in the common I&D case.
 * Trailing zeros are counted as bss.
 */
{
	unsigned long n= hdr->a_data;
	int state= hdr->a_text == 0 ? 0 : sizeof(other_d_magic);
	char c;
	char zero= 0;

	while (n > 0) {
		bread(imagef, image, (char *) &c, sizeof(c));

		/* Look for the magic number byte by byte. */
		if (state < sizeof(other_d_magic)) {
			if (c == other_d_magic[state]) {
				if (++state == sizeof(other_d_magic)) {
					hdr->a_text-= MAGIC_OFF;
					hdr->a_data+= MAGIC_OFF;
				}
			} else {
				/* Not magic, mark as text, reset state. */
				hdr->a_text+= state + 1;
				hdr->a_data-= state + 1;
				state= 0;
			}
		}
		if (c == 0) {
			/* A zero may be part of bss. */
			hdr->a_bss++;
			hdr->a_data--;
		} else {
			while (hdr->a_bss > 0) {
				/* These zeros were not bss, add to data. */
				bwrite(procf, proc, (char *) &zero,
								sizeof(zero));
				hdr->a_data++;
				hdr->a_bss--;
			}
			/* Extract one data byte. */
			bwrite(procf, proc, (char *) &c, sizeof(c));
		}
		n--;
	}
	if (state != sizeof(other_d_magic)) {
		fprintf(stderr, "demolish: can't find the magic number of %s\n",
			proc);
		exit(1);
	}
}

void extr_syms(imagef, image, procf, proc, hdr)
	FILE *imagef; char *image;
	FILE *procf; char *proc;
	struct exec *hdr;
/* Extract the symbol table. */
{
	unsigned long n= hdr->a_syms;
	char c;

	while (n > 0) {
		bread(imagef, image, &c, sizeof(c));
		bwrite(procf, proc, &c, sizeof(c));
		n--;
	}
}

void dem_oldimage(image, procv) char *image; char **procv;
/* Extract an old build image, nice guessing game. */
{
	FILE *imagef, *procf;
	char *proc;
	struct sizes {
		unsigned short text;
		unsigned short data;
	} sizes[PROCESS_MAX];
	int p;
	struct exec hdr;
	unsigned short kernel_ds, k_stktop, syms_off;
	off_t k_data_off;

	if ((imagef= fopen(image, "r")) == nil) fatal(image);

	/* The kernel ds is pached into kernel text, use it to find the
	 * sizes array.
	 */
	position(imagef, image, (off_t) SECTOR_SIZE + P_KDS_OFF, 0);
	bread(imagef, image, (char *) &kernel_ds, sizeof(kernel_ds));

	k_data_off= (off_t) (kernel_ds - MINIXSEG) << HCLICK_SHIFT;
	if (k_data_off == 0) {
		/* The kernel is common I&D, use an address in text. */
		position(imagef, image, (off_t) SECTOR_SIZE + K_STKTOP, 0);
		bread(imagef, image, (char *) &k_stktop, sizeof(k_stktop));
		k_data_off= k_stktop - DTA2TOP;
	}

	/* Extract the sizes array. */
	position(imagef, image, SECTOR_SIZE + k_data_off, 0);
	bread(imagef, image, (char *) sizes, sizeof(sizes));

	position(imagef, image, (off_t) SECTOR_SIZE, 0);

	banner();

	for (p= 0; (proc= *procv++) != nil; p++) {
		/* Extract the offset of the (possibly empty) symbol table
		 * in the text segment of the process.  This gives us the
		 * the precise end of the data segment.
		 */
		position(imagef, image, (off_t) P_SYMS_OFF, 1);
		bread(imagef, image, (char *) &syms_off, sizeof(syms_off));
		position(imagef, image,
				- (off_t) (P_SYMS_OFF + sizeof(syms_off)), 1);

		/* Fill in the header of the executable. */
		memset((void *) &hdr, 0, sizeof(hdr));
		hdr.a_magic[0]= A_MAGIC0;
		hdr.a_magic[1]= A_MAGIC1;
		hdr.a_flags= sizes[p].text == 0 ? A_EXEC : A_SEP;
		hdr.a_cpu= A_I8086;
		hdr.a_hdrlen= A_MINHDR;
		hdr.a_text= (long) sizes[p].text << CLICK_SHIFT;
		hdr.a_data= syms_off;
		hdr.a_bss= 0;
		hdr.a_syms= ((long) sizes[p].data << CLICK_SHIFT) - syms_off;
		hdr.a_total= hdr.a_data;

		if (p == 0 && !(hdr.a_flags & A_SEP)) {
			/* Start of kernel data is known. */
			hdr.a_text= k_data_off;
			hdr.a_data-= k_data_off;
		}

		if ((procf= fopen(proc, "w")) == nil) fatal(proc);

		/* Copy the various pieces of the program. */
		bwrite(procf, proc, (char *) &hdr, (size_t) hdr.a_hdrlen);
		extr_text(imagef, image, procf, proc, &hdr);
		extr_data(imagef, image, procf, proc, &hdr);
		extr_syms(imagef, image, procf, proc, &hdr);

		pr_sizes(&hdr, proc);

		/* Update the header, extr_data has changed the segment sizes.
		 */
		position(procf, proc, (off_t) 0, 0);
		bwrite(procf, proc, (char *) &hdr, (size_t) hdr.a_hdrlen);

		if (p == 0) {
			/* The kernel has been mutilated by build, repair it. */
			unsigned short click_shift= CLICK_SHIFT;
			kernel_ds= 0;

			/* Click shift in text was overwritten by syms offset.
			 */
			position(procf, proc,
				(off_t) (hdr.a_hdrlen + KCLICK_OFF),
				0
			);
			bwrite(procf, proc, (char *) &click_shift,
							sizeof(click_shift));

			/* Monitor uses kernel ds in text as a flags word. */
			position(procf, proc,
				(off_t) (hdr.a_hdrlen + FLAGS_OFF),
				0
			);
			bwrite(procf, proc, (char *) &kernel_ds,
							sizeof(kernel_ds));

			/* Magic number and click shift in data area overwritten
			 * by the sizes array.
			 */
			position(procf, proc,
				(off_t) (hdr.a_hdrlen + hdr.a_text + MAGIC_OFF),
				0
			);
			bwrite(procf, proc, kernel_d_magic,
							sizeof(kernel_d_magic));
			position(procf, proc,
				(off_t) (hdr.a_hdrlen + hdr.a_text + CLICK_OFF),
				0
			);
			bwrite(procf, proc, (char *) &click_shift,
							sizeof(click_shift));
		}
		if (fclose(procf) == EOF) fatal(proc);
	}
}
/* Here ends the old image extracting code.  Obsolete soon, I hope. */

void usage()
{
	fprintf(stderr, "Usage: demolish image [kernel mm fs ... init]\n");
	exit(1);
}

int main(argc, argv) int argc; char **argv;
{
	if (argc == 2)
		dem_newimage(argv[1]);
	else
	if (argc >= 6) {
		if (argc > 2 + PROCESS_MAX) {
			fprintf(stderr,
				"demolish: %s can't have %d (>%d) processes\n",
				argv[1], argc - 2, PROCESS_MAX);
			exit(1);
		}
		dem_oldimage(argv[1], argv + 2);
	} else
		usage();
	exit(0);
}
/* Kees J. Bot 8-2-92. */
