shithub: scc

Download patch

ref: c00d99fba090c89e1fd9e6874b91e8aafadba8d6
parent: 25bc36bff00d311a5d3fd681ecf20b707df455a4
author: Roberto E. Vargas Caballero <k0ga@shike2.com>
date: Fri Jan 4 08:39:16 EST 2019

[libmach] First version of libmach

Libmach is a library to abstract the details of the different
object and executable formats. This is only a first version
and more work is expected.

diff: cannot open b/src/libmach//null: file does not exist: 'b/src/libmach//null'
--- /dev/null
+++ b/include/scc/scc/mach.h
@@ -1,0 +1,30 @@
+typedef struct symbol Symbol;
+typedef struct object Obj;
+
+struct symbol {
+	char type;
+	char *name;
+	unsigned long long size;
+	unsigned long long value;
+};
+
+struct object {
+	int type;
+	Symbol *symtbl;
+	unsigned long nsym;
+	unsigned long cursym;
+	fpos_t pos;
+	void *data;
+};
+
+extern int archive(FILE *fp);
+extern int armember(FILE *fp, char *member);
+
+extern int artraverse(FILE *fp,
+                      int (*fn)(FILE *, char *, void *),
+                      void *data);
+
+extern int objtest(FILE *fp, char **name);
+extern int objopen(FILE *fp, int type, Obj *obj);
+extern int objread(FILE *fp, Obj *obj, int (*filter)(Symbol *));
+extern void objclose(Obj *obj);
--- a/src/Makefile
+++ b/src/Makefile
@@ -4,7 +4,7 @@
 include $(PROJECTDIR)/scripts/rules.mk
 
 TOOLS = cc1 cc2 ld as nm objdump
-LIBS  = libscc libcoff32 libc libcrt
+LIBS  = libscc libcoff32 libc libcrt libmach
 DIRS  = $(TOOLS) $(LIBS)
 
 all: $(TOOLS)
--- /dev/null
+++ b/src/libmach/Makefile
@@ -1,0 +1,17 @@
+.POSIX:
+PROJECTDIR =../..
+include $(PROJECTDIR)/scripts/rules.mk
+
+TARGET = $(LIBDIR)/libmach.a
+OBJS = object.o \
+       archive.o \
+       pack.o \
+       unpack.o \
+       artraverse.o \
+       armember.o \
+       coff32.o \
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+	mklib -o $@ $?
--- /dev/null
+++ b/src/libmach/archive.c
@@ -1,0 +1,24 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <scc/ar.h>
+#include <scc/mach.h>
+#include "libmach.h"
+
+int
+archive(FILE *fp)
+{
+	int n;
+	fpos_t pos;
+	char magic[SARMAG];
+
+	fgetpos(fp, &pos);
+
+	n = fread(magic, SARMAG, 1, fp);
+	if (n == 1 && strncmp(magic, ARMAG, SARMAG) == 0)
+		return 1;
+
+	fsetpos(fp, &pos);
+
+	return 0;
+}
--- /dev/null
+++ b/src/libmach/armember.c
@@ -1,0 +1,46 @@
+#include <stdio.h>
+#include <string.h>
+
+#include <scc/ar.h>
+#include <scc/mach.h>
+
+static char *
+getfname(struct ar_hdr *hdr, char *dst)
+{
+        char *p;
+        int i;
+
+        memcpy(dst, hdr->ar_name, SARNAM);
+        dst[SARNAM] = '\0';
+
+        for (i = SARNAM-1; i >= 0; i--) {
+                if (dst[i] != ' ' && dst[i] != '/')
+                        break;
+                dst[i] = '\0';
+        }
+        return dst;
+}
+
+int
+armember(FILE *fp, char *member)
+{
+        struct ar_hdr hdr;
+        long siz;
+
+        if (fread(&hdr, sizeof(hdr), 1, fp) != 1)
+		return (feof(fp)) ? 0 : -1;
+
+	if (strncmp(hdr.ar_fmag, ARFMAG, sizeof(hdr.ar_fmag)))
+		return -1;
+
+	siz = 0;
+	sscanf(hdr.ar_size, "%10ld", &siz);
+	if (siz & 1)
+		siz++;
+	if (siz == 0)
+		return -1;
+
+	getfname(&hdr, member);
+
+	return 0;
+}
--- /dev/null
+++ b/src/libmach/artraverse.c
@@ -1,0 +1,29 @@
+#include <stdio.h>
+
+#include <scc/ar.h>
+#include <scc/mach.h>
+
+int
+artraverse(FILE *fp, int (*fn)(FILE *, char *, void *), void *data)
+{
+	int r;
+	long off;
+	fpos_t pos;
+	char name[SARNAM+1];
+
+	for (;;) {
+		fgetpos(fp, &pos);
+
+		if ((off = armember(fp, name)) < 0)
+			return -1;
+		r = !(*fn)(fp, name, data);
+		if (!r)
+			return r;
+
+		fsetpos(fp, &pos);
+		fseek(fp, off, SEEK_SET);
+
+		if (off == 0)
+			return 0;
+	}
+}
--- /dev/null
+++ b/src/libmach/coff32.c
@@ -1,0 +1,286 @@
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <scc/coff32/filehdr.h>
+#include <scc/coff32/scnhdr.h>
+#include <scc/coff32/syms.h>
+#include <scc/mach.h>
+
+#include "libmach.h"
+
+typedef struct coff32 Coff32;
+
+struct arch {
+	char *name;
+	unsigned char magic[2];
+	int type;
+};
+
+struct coff32 {
+	FILHDR hdr;
+	SCNHDR *scns;
+	SYMENT *ents;
+	char *strtbl;
+	unsigned long strsiz;
+};
+
+static struct arch archs[] = {
+	"coff32-i386", "\x4c\x01", OBJ(COFF32, ARCH386, LITTLE_ENDIAN),
+	"coff32-z80", "\x5a\x80", OBJ(COFF32, ARCHZ80, LITTLE_ENDIAN),
+	NULL,
+};
+
+static void
+unpack_hdr(int order, unsigned char *buf, FILHDR *hdr)
+{
+	int n;
+
+	n = unpack(order,
+	           buf,
+	           "sslllss",
+	           &hdr->f_magic,
+	           &hdr->f_nscns,
+	           &hdr->f_timdat,
+	           &hdr->f_symptr,
+	           &hdr->f_nsyms,
+	           &hdr->f_opthdr,
+	           &hdr->f_flags);
+	assert(n == FILHSZ);
+}
+
+static void
+unpack_scn(int order, unsigned char *buf, SCNHDR *scn)
+{
+	int n;
+
+	n = unpack(order,
+	          buf,
+	          "'8llllllssl",
+	          scn->s_name,
+	          &scn->s_paddr,
+	          &scn->s_vaddr,
+	          &scn->s_size,
+	          &scn->s_scnptr,
+	          &scn->s_relptr,
+	          &scn->s_lnnoptr,
+	          &scn->s_nrelloc,
+	          &scn->s_nlnno,
+	          &scn->s_flags);
+	assert(n == SCNHSZ);
+}
+
+static void
+unpack_ent(int order, unsigned char *buf, SYMENT *ent)
+{
+	int n;
+	char *s;
+
+	n = unpack(order,
+	          buf,
+	           "'8lsscc",
+	           ent->n_name,
+	           &ent->n_value,
+	           &ent->n_scnum,
+	           &ent->n_type,
+	           &ent->n_sclass,
+	           &ent->n_numaux);
+	assert(n == SYMESZ);
+
+	s = ent->n_name;
+	if (!s[0] && !s[1] && !s[2] && !s[3])
+		unpack(order, "ll", buf, &ent->n_zeroes, &ent->n_offset);
+}
+
+int
+coff32probe(unsigned char *buf, char **name)
+{
+	struct arch *ap;
+
+	for (ap = archs; ap->name; ap++) {
+		if (ap->magic[0] == buf[0] && ap->magic[1] == buf[1]) {
+			if (name)
+				*name = ap->name;
+			return ap->type;
+		}
+	}
+	return -1;
+}
+
+int
+coff32open(FILE *fp, int type, Obj *obj)
+{
+	int order;
+	long i, siz;
+	FILHDR *hdr;
+	SCNHDR *scn = NULL;
+	SYMENT *ent = NULL;
+	char *str = NULL;
+	struct coff32 *coff = NULL;
+	unsigned char buf[100];
+
+	assert(FILHSZ < sizeof(buf));
+	assert(SCNHSZ < sizeof(buf));
+	assert(SYMESZ < sizeof(buf));
+
+	coff = calloc(1, sizeof(*coff));
+	if (!coff)
+		goto error;
+	hdr = &coff->hdr;
+
+	order = ORDER(type);
+	if (fgetpos(fp, &obj->pos))
+		goto error;
+	if (fread(buf, FILHSZ, 1, fp) != 1)
+		goto error;
+	unpack_hdr(order, buf, hdr);
+
+	if (hdr->f_nscns > 0) {
+		scn = calloc(hdr->f_nscns, sizeof(*scn));
+		if (!scn)
+			goto error;
+		coff->scns = scn;
+	}
+	if (hdr->f_nsyms > 0) {
+		ent = calloc(hdr->f_nsyms, sizeof(*ent));
+		if (!ent)
+			goto error;
+		coff->ents = ent;
+	}
+
+	if (fseek(fp, hdr->f_opthdr, SEEK_CUR) < 0)
+		goto error;
+	for (i = 0; i < hdr->f_nscns; i++) {
+		if (fread(buf, SCNHSZ, 1, fp) < 0)
+			goto error;
+		unpack_scn(order, buf, &scn[i]);
+	}
+
+	if (fsetpos(fp, &obj->pos))
+		return -1;
+	if (fseek(fp, hdr->f_symptr, SEEK_CUR) < 0)
+		return -1;
+	for (i = 0; i < hdr->f_nsyms; i++) {
+		if (fread(buf, SYMESZ, 1, fp) != 1)
+			goto error;
+		unpack_ent(order, buf, &ent[i]);
+	}
+
+	if (fread(buf, 4, 1, fp) != 1)
+		goto error;
+	unpack(order, buf, "l", &siz);
+	siz -= 4;
+	if (siz > 0) {
+		if (siz > SIZE_MAX)
+			goto error;
+		str = malloc(siz);
+		if (!str)
+			goto error;
+		if (fread(str, siz, 1, fp) != 1)
+			goto error;
+		coff->strtbl = str;
+		coff->strsiz = siz;
+	}
+	obj->data = coff;
+
+	return 0;
+
+error:
+	free(str);
+	free(scn);
+	free(ent);
+	free(coff);
+
+	return -1;
+}
+
+static char *
+symname(Coff32 *coff, SYMENT *ent)
+{
+	long off;
+
+	if (ent->n_zeroes != 0)
+		return ent->n_name;
+
+	off = ent->n_offset;
+	if (off >= coff->strsiz)
+		return NULL;
+	return &coff->strtbl[off];
+}
+
+static int
+typeof(Coff32 *coff, SYMENT *ent)
+{
+	int c;
+	SCNHDR *scn;
+	long flags;
+
+	switch (ent->n_scnum) {
+	case N_DEBUG:
+		c = 'N';
+		break;
+	case N_ABS:
+		c = 'a';
+		break;
+	case N_UNDEF:
+		c = (ent->n_value != 0) ? 'C' : 'U';
+		break;
+	default:
+		if (ent->n_scnum > coff->hdr.f_nscns)
+			return -1;
+		scn = &coff->scns[ent->n_scnum-1];
+		flags = scn->s_flags;
+		if (flags & STYP_TEXT)
+			c = 't';
+		else if (flags & STYP_DATA)
+			c = 'd';
+		else if (flags & STYP_BSS)
+			c = 'b';
+		else
+			c = '?';
+		break;
+	}
+
+	if (ent->n_sclass == C_EXT)
+		c = toupper(c);
+
+	return c;
+
+}
+
+int
+coff32read(Obj *obj, Symbol *sym)
+{
+	int t;
+	char *s;
+	SYMENT *ent;
+	Coff32 *coff = obj->data;
+
+	if (obj->cursym >= coff->hdr.f_nsyms)
+		return 0;
+	ent = &coff->ents[obj->cursym];
+
+	if ((t = typeof(coff, ent)) < 0)
+		return -1;
+
+	if ((s = symname(coff, ent)) == NULL)
+		return -1;
+
+	obj->cursym += ent->n_numaux + 1;
+	sym->type = t;
+	sym->name = s;
+	sym->value = ent->n_value;
+	sym->size = (sym->type == 'C') ? ent->n_value : 0;
+	return 1;
+}
+
+void
+coff32close(Obj *obj)
+{
+	struct coff32 *coff = obj->data;
+
+	free(coff->scns);
+	free(obj->data);
+}
--- /dev/null
+++ b/src/libmach/libmach.h
@@ -1,0 +1,36 @@
+#define NBYTES 20
+#define OBJ(format,arch,order) ((order) << 10 | (arch) << 5 | (format)) 
+#define FORMAT(t) ((t) & 0x1f)
+#define ARCH(t) (((t) >> 5) & 0x1f)
+#define ORDER(t) (((t) >> 10) & 0x1f)
+
+enum objformat {
+	COFF16,
+	COFF32,
+	ELF32,
+	ELF64,
+	NFORMATS,
+};
+
+enum objarch {
+	ARCH286,
+	ARCH386,
+	ARCHAMD64,
+	ARCHZ80,
+	ARCHARM32,
+	ARCHARM64,
+};
+
+enum order {
+	LITTLE_ENDIAN,
+	BIG_ENDIAN,
+};
+
+extern int pack(int order, unsigned char *dst, char *fmt, ...);
+extern int unpack(int order, unsigned char *src, char *fmt, ...);
+
+/* coff32.c */
+int coff32probe(unsigned char *buf, char **name);
+int coff32open(FILE *fp, int type, Obj *obj);
+int coff32read(Obj *obj, Symbol *sym);
+void coff32close(Obj *obj);
--- /dev/null
+++ b/src/libmach/object.c
@@ -1,0 +1,118 @@
+static char sccsid[] = "@(#) ./libmach/object.c";
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <scc/mach.h>
+
+#include "libmach.h"
+
+struct format {
+	int (*probe)(unsigned char *buf, char **name);
+	int (*open)(FILE *fp, int type, Obj *obj);
+	int (*read)(Obj *obj, Symbol *sym);
+	void (*close)(Obj *obj);
+};
+
+static struct format fmts[] = {
+	[COFF32] = {coff32probe, coff32open, coff32read, coff32close},
+	[NFORMATS] = {NULL},
+};
+
+int
+objtest(FILE *fp, char **name)
+{
+	int n, i;
+	int (*fn)(unsigned char *, char **);
+	fpos_t pos;
+	unsigned char buf[NBYTES];
+
+	fgetpos(fp, &pos);
+	n = fread(buf, NBYTES, 1, fp);
+	fsetpos(fp, &pos);
+
+	if (n != 1 || ferror(fp))
+		return -1;
+
+	for (i = 0; i < NFORMATS; i++) {
+		fn = fmts[i].probe;
+		if (!fn)
+			continue;
+		n = (*fn)(buf, name);
+		if (n == -1)
+			continue;
+		return n;
+	}
+
+	return -1;
+}
+
+int
+objopen(FILE *fp, int type, Obj *obj)
+{
+	struct format *op;
+
+	obj->type = type;
+	obj->symtbl = NULL;
+	obj->data = NULL;
+	obj->nsym = obj->cursym = 0;
+	op = &fmts[FORMAT(type)];
+	if ((*op->open)(fp, type, obj) < 0)
+		return -1;
+	return 0;
+}
+
+static int
+addsym(Obj *obj, Symbol *sym)
+{
+	Symbol *p, *new;
+	char *s;
+	size_t len, siz = obj->nsym * sizeof(*sym);
+
+	if (siz > SIZE_MAX - sizeof(*sym))
+		return -1;
+	siz += sizeof(*sym);
+	if ((p = realloc(obj->symtbl, siz)) == NULL)
+		return -1;
+	obj->symtbl = p;
+
+	new = &p[obj->nsym];
+	new->type = sym->type;
+	len = strlen(sym->name) + 1;
+	if ((new->name = malloc(len)) == NULL)
+		return -1;
+	memcpy(new->name, sym->name, len);
+	new->size = sym->size;
+	new->value = sym->value;
+	obj->nsym++;
+
+	return 0;
+}
+
+int
+objread(FILE *fp, Obj *obj, int (*filter)(Symbol *))
+{
+	int r;
+	Symbol sym, *p;
+	struct format *op;
+
+	op = &fmts[FORMAT(obj->type)];
+	while ((r = (*op->read)(obj, &sym)) > 0) {
+		if (filter && (*filter)(&sym))
+			continue;
+		addsym(obj, &sym);
+	}
+
+	return r;
+}
+
+void
+objclose(Obj *obj)
+{
+	struct format *op;
+
+	op = &fmts[FORMAT(obj->type)];
+	(*op->close)(obj);
+}
--- /dev/null
+++ b/src/libmach/pack.c
@@ -1,0 +1,132 @@
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <scc/mach.h>
+#include "libmach.h"
+
+static int
+lpack(unsigned char *dst, char *fmt, va_list va)
+{
+	unsigned char *bp, *cp;
+	unsigned s;
+	unsigned long l;
+	unsigned long long q;
+	int n;
+
+	bp = dst;
+	while (*fmt) {
+		switch (*fmt++) {
+		case '\'':
+			n = atoi(fmt);
+			while (isdigit(fmt))
+				fmt++;
+			cp = va_arg(va, unsigned char *);
+			while (n--)
+				*bp++ = *cp++;
+			break;
+		case 'c':
+			*bp++ = va_arg(va, unsigned);
+			break;
+		case 's':
+			s = va_arg(va, unsigned);
+			*bp++ = s;
+			*bp++ = s >> 8;
+			break;
+		case 'l':
+			l = va_arg(va, unsigned long);
+			*bp++ = l;
+			*bp++ = l >> 8;
+			*bp++ = l >> 16;
+			*bp++ = l >> 24;
+			break;
+		case 'q':
+			q = va_arg(va, unsigned long long);
+			*bp++ = q;
+			*bp++ = q >> 8;
+			*bp++ = q >> 16;
+			*bp++ = q >> 24;
+			*bp++ = q >> 32;
+			*bp++ = q >> 40;
+			*bp++ = q >> 48;
+			*bp++ = q >> 56;
+			break;
+		default:
+			va_end(va);
+			return -1;
+		}
+	}
+
+	return bp - dst;
+}
+
+static int
+bpack(unsigned char *dst, char *fmt, va_list va)
+{
+	unsigned char *bp, *cp;
+	unsigned s;
+	unsigned long l;
+	unsigned long long q;
+	int n;
+
+	bp = dst;
+	while (*fmt) {
+		switch (*fmt++) {
+		case '\'':
+			n = atoi(fmt);
+			while (isdigit(*fmt))
+				fmt++;
+			cp = va_arg(va, unsigned char *);
+			while (n--)
+				*bp++ = *cp++;
+			break;
+		case 'c':
+			*bp++ = va_arg(va, unsigned);
+			break;
+		case 's':
+			s = va_arg(va, unsigned);
+			*bp++ = s >> 8;
+			*bp++ = s;
+			break;
+		case 'l':
+			l = va_arg(va, unsigned long);
+			*bp++ = l >> 24;
+			*bp++ = l >> 16;
+			*bp++ = l >> 8;
+			*bp++ = l;
+			break;
+		case 'q':
+			q = va_arg(va, unsigned long long);
+			*bp++ = q >> 56;
+			*bp++ = q >> 48;
+			*bp++ = q >> 40;
+			*bp++ = q >> 32;
+			*bp++ = q >> 24;
+			*bp++ = q >> 16;
+			*bp++ = q >> 8;
+			*bp++ = q;
+			break;
+		default:
+			va_end(va);
+			return -1;
+		}
+	}
+
+	return bp - dst;
+}
+
+int
+pack(int order, unsigned char *dst, char *fmt, ...)
+{
+	int r;
+	int (*fn)(unsigned char *dst, char *fmt, va_list va);
+	va_list va;
+
+	va_start(va, fmt);
+	fn = (order == LITTLE_ENDIAN) ? lpack : bpack;
+	r = (*fn)(dst, fmt, va);
+	va_end(va);
+
+	return r;
+}
--- /dev/null
+++ b/src/libmach/unpack.c
@@ -1,0 +1,144 @@
+static char sccsid[] = "@(#) ./lib/scc/lunpack.c";
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <scc/mach.h>
+#include "libmach.h"
+
+static int
+lunpack(unsigned char *src, char *fmt, va_list va)
+{
+	unsigned char *bp, *cp;
+	unsigned short *sp;
+	unsigned s;
+	unsigned long *lp, l;
+	unsigned long long *qp, q;
+	int n;
+
+	bp = src;
+	while (*fmt) {
+		switch (*fmt++) {
+		case '\'':
+			n = atoi(fmt);
+			while (isdigit(*fmt))
+				fmt++;
+			cp = va_arg(va, unsigned char *);
+			while (n--)
+				*cp++ = *bp++;
+			break;
+		case 'c':
+			cp = va_arg(va, unsigned char *);
+			*cp = *bp++;
+			break;
+		case 's':
+			sp = va_arg(va, unsigned short *);
+			s =  (unsigned) *bp++;
+			s |= (unsigned) *bp++ << 8;
+			*sp = s;
+			break;
+		case 'l':
+			lp = va_arg(va, unsigned long *);
+			l = (unsigned long) *bp++;
+			l |= (unsigned long) *bp++ << 8;
+			l |= (unsigned long) *bp++ << 16;
+			l |= (unsigned long) *bp++ << 24;
+			*lp = l;
+			break;
+		case 'q':
+			qp = va_arg(va, unsigned long long *);
+			q = (unsigned long long) *bp++;
+			q |= (unsigned long long) *bp++ << 8;
+			q |= (unsigned long long) *bp++ << 16;
+			q |= (unsigned long long) *bp++ << 24;
+			q |= (unsigned long long) *bp++ << 32;
+			q |= (unsigned long long) *bp++ << 40;
+			q |= (unsigned long long) *bp++ << 48;
+			q |= (unsigned long long) *bp++ << 56;
+			*qp = q;
+			break;
+		default:
+			va_end(va);
+			return -1;
+		}
+	}
+
+	return bp - src;
+}
+
+static int
+bunpack(unsigned char *src, char *fmt, va_list va)
+{
+	unsigned char *bp, *cp;
+	unsigned short *sp;
+	unsigned s;
+	unsigned long *lp, l;
+	unsigned long long *qp, q;
+	int n;
+
+	bp = src;
+	while (*fmt) {
+		switch (*fmt++) {
+		case '\'':
+			n = atoi(fmt);
+			while (isdigit(*fmt))
+				fmt++;
+			cp = va_arg(va, unsigned char *);
+			while (n--)
+				*cp++ = *bp++;
+			break;
+		case 'c':
+			cp = va_arg(va, unsigned char *);
+			*cp = *bp++;
+			break;
+		case 's':
+			sp = va_arg(va, unsigned short *);
+			s =  (unsigned) *bp++ << 8;
+			s |= (unsigned) *bp++;
+			*sp = s;
+			break;
+		case 'l':
+			lp = va_arg(va, unsigned long *);
+			l =  (unsigned long) *bp++ << 24;
+			l |= (unsigned long) *bp++ << 16;
+			l |= (unsigned long) *bp++ << 8;
+			l |= (unsigned long) *bp++;
+			*lp = l;
+			break;
+		case 'q':
+			qp = va_arg(va, unsigned long long *);
+			q =  (unsigned long long) *bp++ << 56;
+			q |= (unsigned long long) *bp++ << 48;
+			q |= (unsigned long long) *bp++ << 40;
+			q |= (unsigned long long) *bp++ << 32;
+			q |= (unsigned long long) *bp++ << 24;
+			q |= (unsigned long long) *bp++ << 16;
+			q |= (unsigned long long) *bp++ << 8;
+			q |= (unsigned long long) *bp++;
+			*qp = q;
+			break;
+		default:
+			va_end(va);
+			return -1;
+		}
+	}
+
+	return bp - src;
+}
+
+int
+unpack(int order, unsigned char *src, char *fmt, ...)
+{
+	int r;
+        int (*fn)(unsigned char *dst, char *fmt, va_list va);
+        va_list va;
+
+        va_start(va, fmt);
+        fn = (order == LITTLE_ENDIAN) ? lunpack : bunpack;
+        r = (*fn)(src, fmt, va);
+        va_end(va);
+
+        return r;
+}
--- a/src/nm/Makefile
+++ b/src/nm/Makefile
@@ -4,17 +4,15 @@
 include $(PROJECTDIR)/scripts/rules.mk
 
 OBJS  = main.o \
-        coff32.o \
-        formats.o \
 
 TARGET = $(BINDIR)/nm
 
 all: $(TARGET)
 
-$(TARGET): $(LIBDIR)/libscc.a
+$(TARGET): $(LIBDIR)/libscc.a $(LIBDIR)/libmach.a
 
 $(TARGET): $(OBJS)
-	$(CC) $(SCC_LDFLAGS) $(OBJS) -lscc -o $@
+	$(CC) $(SCC_LDFLAGS) $(OBJS) -lmach -lscc -o $@
 
 dep: inc-dep
 
--- a/src/nm/coff32.c
+++ /dev/null
@@ -1,312 +1,0 @@
-static char sccsid[] = "@(#) ./nm/coff.c";
-
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <scc/coff32/filehdr.h>
-#include <scc/coff32/scnhdr.h>
-#include <scc/coff32/syms.h>
-#include <scc/scc.h>
-#include "nm.h"
-
-static int (*unpack)(unsigned char *, char *, ...);
-static long strtbl, symtbl, sectbl;
-static SCNHDR *sections;
-static struct symbol *syms;
-static size_t nsect, nsyms;
-
-static char
-typeof(SYMENT *ent)
-{
-	SCNHDR *sec;
-	int c;
-	long flags;
-
-	switch (ent->n_scnum) {
-	case N_DEBUG:
-		c = 'n';
-		break;
-	case N_ABS:
-		c = 'a';
-		break;
-	case N_UNDEF:
-		c = (ent->n_value != 0) ? 'C' : 'U';
-		break;
-	default:
-		if (ent->n_scnum > nsect)
-			die("nm: incorrect section index");
-		sec = &sections[ent->n_scnum-1];
-		flags = sec->s_flags;
-		if (flags & STYP_TEXT)
-			c = 't';
-		else if (flags & STYP_DATA)
-			c = 'd';
-		else if (flags & STYP_BSS)
-			c = 'b';
-		else
-			c = '?';
-		break;
-	}
-
-	if (ent->n_sclass == C_EXT)
-		c = toupper(c);
-
-	return c;
-}
-
-static char *
-getsname(char *fname, FILE *fp, SYMENT *ent)
-{
-	int c;
-	size_t len;
-	char *s, *err;
-	fpos_t pos;
-
-	if (ent->n_zeroes != 0) {
-		for (len = 0; len < E_SYMNMLEN && ent->n_name[len]; ++len)
-			;
-		s = xmalloc(len+1);
-		s[len] = '\0';
-		return memcpy(s, ent->n_name, len);
-	}
-
-	/* TODO: read the string table in memory before reading symbols */
-	fgetpos(fp, &pos);
-	fseek(fp, strtbl, SEEK_SET);
-	fseek(fp, ent->n_offset, SEEK_CUR);
-
-	if (ferror(fp))
-		goto error;
-
-	s = NULL;
-	for (len = 1; (c = getc(fp)) != EOF; len++) {
-		s = xrealloc(s, len);
-		if ((s[len-1] = c) == '\0')
-			break;
-	}
-	if (c == EOF)
-		goto error;
-	fsetpos(fp, &pos);
-	return s;
-
-error:
-	err = (!ferror(fp)) ?
-		"EOF before reading strings" : strerror(errno);
-	die("nm: %s: %s", fname, err);
-}
-
-static void
-getfsym(unsigned char *buff, SYMENT *ent)
-{
-	int n;
-
-	n = (*unpack)(buff,
-	              "'8lsscc",
-	              &ent->n_name,
-	              &ent->n_value,
-	              &ent->n_scnum,
-	              &ent->n_type,
-	              &ent->n_sclass,
-	              &ent->n_numaux);
-	assert(n == SYMESZ);
-}
-
-static void
-getsymbol(char *fname, FILE *fp,
-          unsigned char *buff, SYMENT *ent, struct symbol *sym)
-{
-	char *nam;
-
-	getfsym(buff, ent);
-	nam = ent->n_name;
-	if (nam[0] == 0 && nam[1] == 0 && nam[2] == 0 && nam[3] == 0) {
-		long zero, offset;
-
-		(*unpack)(nam, "ll", &zero, &offset);
-		ent->n_zeroes = zero;
-		ent->n_offset = offset;
-	}
-	sym->name = getsname(fname, fp, ent);
-	sym->type = typeof(ent);
-	sym->value = ent->n_value;
-	sym->size = (sym->type == 'C') ? ent->n_value : 0;
-}
-
-static void
-getsyms(char *fname, char *member, FILE *fp, FILHDR *hdr)
-{
-	size_t n, i;
-	unsigned aux;
-	unsigned char buff[SYMESZ];
-	SYMENT ent;
-
-	if (hdr->f_nsyms > SIZE_MAX)
-		die("nm: %s:Too many symbols\n", member);
-
-	n = hdr->f_nsyms;
-	syms = xcalloc(sizeof(*syms), n);
-
-	if (fseek(fp, symtbl, SEEK_SET) == EOF)
-		die("nm: %s:%s", fname, strerror(errno));
-
-	aux = nsyms = 0;
-	for (i = 0; i < n; i++) {
-		if (fread(buff, SYMESZ, 1, fp) != 1)
-			break;
-		if (aux > 0) {
-			aux--;
-			continue;
-		}
-		getsymbol(member, fp, buff, &ent, &syms[nsyms++]);
-		aux = ent.n_numaux;
-	}
-	if (n != i) {
-		char *err;
-
-		err = (!ferror(fp)) ?
-			"EOF before reading symbols" : strerror(errno);
-		die("nm: %s: %s", fname, err);
-	}
-}
-
-static void
-getfsec(unsigned char *buff, SCNHDR *sec)
-{
-	int n;
-
-	n = (*unpack)(buff,
-	              "'8llllllssl",
-	              sec->s_name,
-	              &sec->s_paddr,
-	              &sec->s_vaddr,
-	              &sec->s_size,
-	              &sec->s_scnptr,
-	              &sec->s_relptr,
-	              &sec->s_lnnoptr,
-	              &sec->s_nrelloc,
-	              &sec->s_nlnno,
-	              &sec->s_flags);
-	assert(n == SCNHSZ);
-}
-
-static void
-getsects(char *fname, char *member, FILE *fp, FILHDR *hdr)
-{
-	size_t i;
-	char buff[SCNHSZ];
-
-	nsect = hdr->f_nscns;
-	if (nsect == 0)
-		return;
-
-	if (nsect > SIZE_MAX)
-		die("nm: %s:Too many sections\n", member);
-
-	if (fseek(fp, sectbl, SEEK_SET) == EOF)
-		die("nm: %s:%s", member, strerror(errno));
-
-	sections = xcalloc(sizeof(*sections), nsect);
-	for (i = 0; i < nsect; i++) {
-		if (fread(buff, SCNHSZ, 1, fp) != 1)
-			break;
-		getfsec(buff, &sections[i]);
-	}
-	if (i != nsect) {
-		char *err;
-
-		err = (!ferror(fp)) ?
-			"EOF before reading sections" : strerror(errno);
-		die("nm: %s: %s", fname, err);
-	}
-}
-
-static void
-getfhdr(unsigned char *buff, FILHDR *hdr)
-{
-	int n;
-
-	n = (*unpack)(buff,
-	          "sslllss",
-	          &hdr->f_magic,
-	          &hdr->f_nscns,
-	          &hdr->f_timdat,
-	          &hdr->f_symptr,
-	          &hdr->f_nsyms,
-	          &hdr->f_opthdr,
-	          &hdr->f_flags);
-	assert(n == FILHSZ);
-}
-
-static int
-nm(char *fname, char *member, FILE *fp)
-{
-	unsigned char buff[FILHSZ];
-	FILHDR hdr;
-	long pos = ftell(fp);
-
-	if (fread(buff, FILHSZ, 1, fp) != 1) {
-		if (!ferror(fp))
-			return 0;
-		die("nm: %s: %s", fname, strerror(errno));
-	}
-
-	getfhdr(buff, &hdr);
-	if ((hdr.f_flags & F_SYMS) != 0 || hdr.f_nsyms == 0) {
-		fprintf(stderr, "nm: %s: no symbols\n", member);
-		return 1;
-	}
-
-	/* TODO: Check overflow */
-	strtbl = pos + hdr.f_symptr + hdr.f_nsyms* SYMESZ;
-	symtbl = pos + hdr.f_symptr;
-	sectbl = pos + FILHSZ + hdr.f_opthdr;
-
-	getsects(fname, member, fp, &hdr);
-	getsyms(fname, member, fp, &hdr);
-	printsyms(fname, member, syms, nsyms);
-
-	free(sections);
-	free(syms);
-	return 1;
-}
-
-static int
-probe(char *fname, char *member, FILE *fp)
-{
-	int c;
-	int c1, c2;
-	fpos_t pos;
-	unsigned short magic;
-
-	fgetpos(fp, &pos);
-	c1 = getc(fp);
-	c2 = getc(fp);
-	fsetpos(fp, &pos);
-
-	if (ferror(fp))
-		die("nm: %s: %s", fname, strerror(errno));
-
-	if (c1 == EOF || c2 == EOF)
-		return 0;
-	magic = c1 | c2 << 8;
-
-	switch (magic) {
-	case COFF_I386MAGIC:
-	case COFF_Z80MAGIC:
-		unpack = lunpack;
-		return 1;
-	default:
-		unpack = NULL;
-		return 0;
-	}
-}
-
-struct objfile coff32 = {
-	.probe = probe,
-	.nm = nm,
-};
--- a/src/nm/deps.mk
+++ b/src/nm/deps.mk
@@ -1,11 +1,5 @@
 #deps
-coff32.o: $(INCDIR)/scc/scc/coff32/filehdr.h
-coff32.o: $(INCDIR)/scc/scc/coff32/scnhdr.h
-coff32.o: $(INCDIR)/scc/scc/coff32/syms.h
-coff32.o: $(INCDIR)/scc/scc/scc.h
-coff32.o: nm.h
-formats.o: nm.h
 main.o: $(INCDIR)/scc/scc/ar.h
 main.o: $(INCDIR)/scc/scc/arg.h
+main.o: $(INCDIR)/scc/scc/mach.h
 main.o: $(INCDIR)/scc/scc/scc.h
-main.o: nm.h
--- a/src/nm/formats.c
+++ /dev/null
@@ -1,13 +1,0 @@
-static char sccsid[] = "@(#) ./nm/probe.c";
-
-#include <stdio.h>
-
-#include "nm.h"
-
-/* TODO: Autogenerate this file */
-struct objfile coff32;
-
-struct objfile *formats[] = {
-	&coff32,
-	NULL,
-};
--- a/src/nm/main.c
+++ b/src/nm/main.c
@@ -2,7 +2,7 @@
 
 #include <ctype.h>
 #include <errno.h>
-#include <limits.h>
+#include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -11,9 +11,10 @@
 #include <scc/arg.h>
 #include <scc/scc.h>
 #include <scc/ar.h>
-#include "nm.h"
+#include <scc/mach.h>
 
 char *argv0;
+static int status;
 static int radix = 16;
 static int Pflag;
 static int Aflag;
@@ -20,196 +21,168 @@
 static int vflag;
 static int gflag;
 static int uflag;
-static int arflag;
+static char *filename, *membname;
 
-static int
-object(char *fname, char *member, FILE *fp)
+static void
+error(char *fmt, ...)
 {
-	extern struct objfile *formats[];
-	struct objfile **p, *obj;
-	void *data;
+	va_list va;
 
-	for (p = formats; *p; ++p) {
-		obj = *p;
-		if ((*obj->probe)(fname, member, fp))
-			break;
-	}
-	if (*p == NULL)
-		return 0;
-	return (*obj->nm)(fname, member, fp);
+	va_start(va, fmt);
+	fprintf(stderr, "nm: %s: ", filename);
+	if (membname)
+		fprintf(stderr, "%s: ", membname);
+	vfprintf(stderr, fmt, va);
+	putc('\n', stderr);
+	va_end(va);
+
+	status = 1;
 }
 
-static char *
-getfname(struct ar_hdr *hdr, char *dst)
+static int
+cmp(const void *p1, const void *p2)
 {
-	char *p;
-	int i;
+	const Symbol *s1 = p1, *s2 = p2;
 
-	memcpy(dst, hdr->ar_name, SARNAM);
-	dst[SARNAM] = '\0';
-
-	for (i = SARNAM-1; i >= 0; i--) {
-		if (dst[i] != ' ' && dst[i] != '/')
-			break;
-		dst[i] = '\0';
+	if (vflag) {
+		if (s1->value > s2->value)
+			return 1;
+		if (s1->value < s2->value)
+			return -1;
+		if (s1->type == 'U' && s2->type == 'U')
+			return 0;
+		if (s1->type == 'U')
+			return -1;
+		if (s2->type == 'U')
+			return 1;
+		return 0;
+	} else {
+		return strcmp(s1->name, s2->name);
 	}
-	return dst;
 }
 
 static void
-ar(char *fname, FILE *fp)
+printsyms(Obj *obj)
 {
-	struct ar_hdr hdr;
-	long pos, siz;
-	char member[SARNAM+1];
+	unsigned long nsym;
+	Symbol *sym;
 
-	arflag = 1;
-	if (fseek(fp, SARMAG, SEEK_SET) == EOF)
-		goto file_error;
+	if (!obj->symtbl)
+		return;
+	sym = obj->symtbl;
+	nsym = obj->nsym;
 
-	while (fread(&hdr, sizeof(hdr), 1, fp) == 1) {
-		pos = ftell(fp);
-		if (strncmp(hdr.ar_fmag, ARFMAG, sizeof(hdr.ar_fmag)))
-			goto corrupted;
+	qsort(sym, nsym, sizeof(*sym), cmp);
 
-		siz = 0;
-		sscanf(hdr.ar_size, "%10ld", &siz);
-		if (siz == 0)
-			goto corrupted;
+	for (sym = obj->symtbl; nsym--; sym++) {
+		int type = sym->type;
+		char *fmt;
 
-		if (siz & 1)
-			siz++;
-		if (pos == -1 || pos > LONG_MAX - siz)
-			die("nm: %s: overflow in size of archive", fname);
-		pos += siz;
+		if (Aflag) {
+			fmt = (membname) ? "%s[%s]: " : "%s: ";
+			printf(fmt, filename, membname);
+		}
 
-		getfname(&hdr, member);
-		if (!object(fname, member, fp)) {
-			fprintf(stderr,
-			        "nm: skipping member %s in archive %s\n",
-			        member, fname);
+		if (Pflag) {
+			printf("%s %c", sym->name, sym->type);
+			if (type != 'U') {
+				if (radix == 8)
+					fmt = " %016.16llo %lo";
+				else if (radix == 10)
+					fmt = " %016.16llu %lu";
+				else
+					fmt = " %016.16llx %lx";
+				printf(fmt, sym->value, sym->size);
+			}
+		} else {
+			if (type == 'U')
+				fmt = "                ";
+			else if (radix == 8)
+				fmt = "%016.16llo";
+			else if (radix == 10)
+				fmt = "%016.16lld";
+			else
+				fmt = "%016.16llx";
+			printf(fmt, sym->value);
+			printf(" %c %s", sym->type, sym->name);
 		}
-		if (fseek(fp, pos, SEEK_SET) == EOF)
-			goto file_error;
+		putchar('\n');
 	}
-	if (ferror(fp))
-		goto file_error;
-	return;
-
-corrupted:
-	die("nm: %s: corrupted archive", fname);
-file_error:
-	die("nm: %s: %s", fname, strerror(errno));
 }
 
 static int
-archive(char *fname, FILE *fp)
+filter(Symbol *sym)
 {
-	char magic[SARMAG];
-	fpos_t pos;
+	int type = sym->type;
 
-	fgetpos(fp, &pos);
-	fread(magic, SARMAG, 1, fp);
-	fsetpos(fp, &pos);
+	if (type == '?' || type == 'N')
+		return 1;
 
-	if (ferror(fp))
-		die("nm: %s: %s", fname, strerror(errno));
-	if (strncmp(magic, ARMAG, SARMAG) != 0)
-		return 0;
+	if (uflag && type != 'U')
+		return 1;
 
-	ar(fname, fp);
-	return 1;
+	if (gflag && !isupper(type))
+		return 1;
+
+	return 0;
 }
 
 static void
-printsym(char *file, char *member, struct symbol *sym)
+newobject(FILE *fp, int type)
 {
-	char *fmt;
-	int type = sym->type;
+	Obj obj;
 
-	if (type == '?')
-		return;
+	if (objopen(fp, type, &obj) < 0)
+		goto err1;
 
-	if (uflag && type != 'U')
-		return;
+	if (objread(fp, &obj, filter) < 0)
+		goto err2;
 
-	if (gflag && !isupper(type))
-		return;
+	printsyms(&obj);
+	objclose(&obj);
+	return;
 
-	if (Aflag)
-		printf((arflag) ? "%s[%s]: " : "%s: ", file, member);
-	if (Pflag) {
-		printf("%s %c", sym->name, sym->type);
-		if (type != 'U') {
-			if (radix == 8)
-				fmt = " %016.16llo %lo";
-			else if (radix == 10)
-				fmt = " %016.16llu %lu";
-			else
-				fmt = " %016.16llx %lx";
-			printf(fmt, sym->value, sym->size);
-		}
-	} else {
-		if (type == 'U')
-			fmt = "                ";
-		else if (radix == 8)
-			fmt = "%016.16llo";
-		else if (radix == 10)
-			fmt = "%016.16lld";
-		else
-			fmt = "%016.16llx";
-		printf(fmt, sym->value);
-		printf(" %c %s", sym->type, sym->name);
-	}
-	putchar('\n');
+err2:
+	objclose(&obj);
+err1:
+	error("object file corrupted");
 }
 
 static int
-cmp(const void *p1, const void *p2)
+newmember(FILE *fp, char *name, void *data)
 {
-	const struct symbol *s1 = p1, *s2 = p2;
+	int t;
 
-	if (vflag) {
-		if (s1->value > s2->value)
-			return 1;
-		if (s1->value < s2->value)
-			return -1;
-		if (s1->type == 'U' && s2->type == 'U')
-			return 0;
-		if (s1->type == 'U')
-			return -1;
-		if (s2->type == 'U')
-			return 1;
-		return 0;
-	} else {
-		return strcmp(s1->name, s2->name);
-	}
-}
+	membname = name;
+	if ((t = objtest(fp, NULL)) != -1)
+		newobject(fp, t);
 
-void
-printsyms(char *file, char *member, struct symbol *syms, size_t nsyms)
-{
-	qsort(syms, nsyms, sizeof(*syms), cmp);
-
-	while (nsyms--)
-		printsym(file, member, syms++);
+	return 0;
 }
 
 static void
-doit(char *fname)
+nm(char *fname)
 {
+	int t;
 	FILE *fp;
 
-	arflag = 0;
+	filename = fname;
+	membname = NULL;
 
-	if ((fp = fopen(fname, "rb")) == NULL)
-		die("nm: %s: %s", fname, strerror(errno));
+	if ((fp = fopen(fname, "rb")) == NULL) {
+		error(strerror(errno));
+		return;
+	}
 
-	if (!object(fname, fname, fp) && !archive(fname, fp))
-		die("nm: %s: File format not recognized", fname);
+	if ((t = objtest(fp, NULL)) != -1)
+		newobject(fp, t);
+	else if (archive(fp))
+		artraverse(fp, newmember, NULL);
+	else
+		error("bad format");
 
 	if (ferror(fp))
-		die("nm: %s: %s", fname, strerror(errno));
+		error(strerror(errno));
 
 	fclose(fp);
 }
@@ -258,15 +231,17 @@
 	} ARGEND
 
 	if (argc == 0) {
-		doit("a.out");
+		nm("a.out");
 	} else {
 		for ( ; *argv; ++argv)
-			doit(*argv);
+			nm(*argv);
 	}
 
 	fflush(stdout);
-	if (ferror(stdout))
-		die("nm: error writing in output");
+	if (ferror(stdout)) {
+		fprintf(stderr, "nm: error writing in output");
+		status = 1;
+	}
 
-	return 0;
+	return status;
 }
--- a/src/nm/nm.h
+++ /dev/null
@@ -1,14 +1,0 @@
-struct symbol {
-	char *name;
-	int type;
-	unsigned long long value;
-	unsigned long size;
-};
-
-struct objfile {
-	int (*probe)(char *fname, char *member, FILE *fp);
-	int (*nm)(char *fname, char *member, FILE *fp);
-};
-
-/* main.c */
-extern void printsyms(char *, char *, struct symbol *, size_t );