Plan 9 from Bell Labs’s /usr/web/sources/contrib/de0u/srec/srec.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/* srec - convert Plan 9 executables to SREC format, aka s-record or S19 format
 *
 * http://en.wikipedia.org/wiki/SREC_(file_format)
 * http://www.linux-mips.org/wiki/SREC
 */

#include	<u.h>
#include	<libc.h>
#include	<bio.h>
#include	<mach.h>

#define U32INT_MAX (0xFFFFFFFFULL)

int verbose = 0;

void
mumble(char *fmt, ...)
{
	if (verbose) {
		va_list arg;

		va_start(arg, fmt);
		vfprint(2, fmt, arg);
		va_end(arg);
	}
}
#pragma varargck argpos mumble 1

void
xBprint(Biobufhdr *bp, char *fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	if (Bvprint(bp, fmt, arg) == Beof)
		sysfatal("Bvprint: %r");
	va_end(arg);
}
#pragma varargck argpos xBprint 2

uchar
cksum(uchar *bytes, int n)
{
	uchar sum;

	for (sum = 0; n > 0; --n)
		sum += *bytes++;

	return(~sum);
}

/* Each "S record" looks like:
 *
 * 'S'
 * one decimal digit of type
 * two hex digits of a single-byte count of bytes in the rest of the record
 * some "address" bytes (variable by type), each byte as two hex digits, big-endian
 * some data bytes, each byte as two hex digits
 * one byte of checksum, as two hex digits
 *
 * Checksum isn't additive for multiple blocks, so we assemble entire record
 * for one checksum operation, then sum it, then print it.  Sorry about the
 * extra copy.
 */

int sent;

void
record(int type, u32int addr, uchar *bytes, int nbytes, Biobufhdr *obp) 
{
	int addrlen = 0, i;
	uchar count, sum;
	uchar line[80], *bp;

	assert(nbytes <= 64);

	switch (type) {
	case 0:
		sent = 0;	/* S0 = start of transmission */
		addrlen = 2;
		break;
	case 1: case 5: case 9:
		addrlen = 2;
		break;
	case 2: case 8:
		addrlen = 3;
		break;
	case 3: case 7:
		addrlen = 4;
		break;
	default:
		sysfatal("Unimplemented S-record %d", type);
	}
	
	count = addrlen + nbytes + sizeof(sum);

	bp = line;

	/* count */
	*bp++ = count;

	/* addr, big-endian */
	while (addrlen > 0) {
		*bp++ = (addr >> ((addrlen - 1) * 8)) & 0xFF;
		--addrlen;
	}

	/* data */
	for (i = 0; i < nbytes; i++)
		*bp++ = bytes[i];

	/* checksum */
	*bp++ = cksum(line, bp - line);

	/* emit */
	xBprint(obp, "S%1d", type);
	for (i = 0; i < bp - line; i++)
		xBprint(obp, "%02uX", line[i]);
	xBprint(obp, "\n");

	++sent;
}

void
header(char *fname, Biobufhdr *obp)
{
	char h[12];

	snprint(h, 10+1, "%-10s", fname); /* +1: snprint() may temporarily place '\0' in h[10] */
	mumble("Encoding name as \"%s\"\n", h);

	h[10] = 9; /* version */
	h[11] = 9; /* revision */

	record(0, 0, (uchar *) h, sizeof(h), obp);
}

void
emit(Map *m, uvlong addr, long n, Biobufhdr *obp)
{
	uchar buf[16]; /* 64 max by "standard"; objcopy uses 16 */

	assert(addr <= U32INT_MAX);
	mumble("Emit %ld bytes from %ullx\n", n, addr);

	while (n > 0) {
		int get = n;

		if (get > sizeof(buf))
			get = sizeof(buf);

		if (get1(m, addr, buf, get) != get)
			sysfatal("Can't get1(%d bytes) from executable at %0llx: %r", get, addr);

		record(3, (u32int) addr, buf, get, obp);
		n -= get;
		addr += get;
	}
}

void
recordcount(Biobufhdr *obp)
{
		record(5, (u32int) sent, nil, 0, obp);
}

/*
 * SREC is inherently 32-bit.  Plan 9 a.out is, too (entry is "long").
 * However, libmach's entry is 64-bit ("uvlong"); when long is
 * promoted to uvlong it gets sign-extended if, say, KZERO is
 * 0xFxxxxxxx, which these days it is.  So we'll take addresses
 * which aren't really 32-bit as long as they are off by only sign
 * extension.
 */
void
entry(uvlong e, Biobufhdr *obp)
{
	if (e & (1<<31))
		if ((e & (U32INT_MAX<<32)) == (U32INT_MAX<<32))
			e &= ~(U32INT_MAX<<32);

	assert(e <= U32INT_MAX);

	mumble("Entry %ullx\n", e);

	record(7, (u32int) e, nil, 0, obp);
}

/* 
 * Sigh: loadmap() returns a Map with EXACTLY two slots, and
 * setmap() won't grow a Map.
 */
Map *loadmapbss(Map *map, int fd, Fhdr *fp)
{
	int zero;

	map = newmap(map, 10);
	if (map == 0)
		return 0;

	if ((zero = open("/dev/zero", OREAD)) < 0) {
		free(map);
		return 0;
	}

	setmap(map, fd, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtoff, "text");
	setmap(map, fd, fp->dataddr, fp->dataddr+fp->datsz, fp->datoff, "data");
	setmap(map, zero, fp->dataddr+fp->datsz, fp->dataddr+fp->datsz+fp->bsssz, 0, "bss");

	return map;
}

void
emitfile(char *fname, int ifd, Biobufhdr *obp)
{
	int seg;
	Fhdr f;
	Map *m;

	if(!crackhdr(ifd, &f)) {
		fprint(2, "%s: %s not an executable?\n", argv0, fname);
		exits("bad format");
	}

	machbytype(f.type);

	mumble("%s executable\n", mach->name);
	mumble("%ldt + %ldd + %ldb = %ld\t%s\n", f.txtsz, f.datsz,
		f.bsssz, f.txtsz+f.datsz+f.bsssz, fname);

	if(!(m = loadmapbss(nil, ifd, &f))) {
		fprint(2, "Cannot loadmapbss()\n");
		exits("bad format");
	}

	for(seg=0; seg < m->nsegs; ++seg) {
		if (m->seg[seg].inuse) {
			mumble("segment %d is named %s\n", seg, m->seg[seg].name);
			mumble("\tbase: 0x%llux, end: 0x%llux\n", m->seg[seg].b, m->seg[seg].e);
			m->seg[seg].cache = 1;
		}
	}

	header(fname, obp);
	emit(m, f.txtaddr, f.txtsz, obp);
	emit(m, f.dataddr, f.datsz, obp);
	emit(m, f.dataddr+f.datsz, f.bsssz, obp);
	recordcount(obp);
	entry(f.entry, obp);
}

void
usage(void)
{
	fprint(2, "usage: %s [-v] [executable-file [output-file]]\n", argv0);
	exits("usage");
}

void
main(int argc, char *argv[])
{
	int ifd;
	Biobuf obuf, *obp;
	char *iname, *oname;

	ifd = -1;
	obp = nil;
	iname = oname = nil;

	ARGBEGIN {
	case 'v':
		++verbose;
		break;
	default:
		usage();
	} ARGEND;

	switch (argc) {
	case 0:
		/* stdin to stdout */
		iname = "(stdin)";
		ifd = 0;
		oname = "(stdout)";
		Binit(&obuf, 1, OWRITE);
		obp = &obuf;
		break;
	case 1:
		/* named file to stdout */
		iname = argv[0]; oname = "(stdout)";
		Binit(&obuf, 1, OWRITE);
		obp = &obuf;
		break;
	case 2:
		/* named input and output */
		iname = argv[0];
		oname = argv[1];
		break;
	default:
		usage();
	}

	/* check input availability before truncating old output file */
	if (ifd < 0) {
		if((ifd = open(iname, OREAD)) < 0){
			fprint(2, "%s: ", argv0);
			perror(iname);
			exits("open()");
		}
	}
	if (obp == nil) {
		if ((obp = Bopen(oname, OWRITE)) == nil) {
			fprint(2, "%s: ", argv0);
			perror(oname);
			exits("open()");
		}
	}

	emitfile(iname, ifd, obp);
	close(ifd);
	if (Bterm(obp) < 0)
		sysfatal("Bterm: %r");

	exits(nil);
}


/* "Test suite"
 *
 * echo 'moo' | srec
 * srec -v srec /dev/vgactl
 * echo still here > foo ; srec /no/such/file foo ; cat foo
 *
 */

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.