rename libmach_amd64 libmach

R=rsc
OCL=33273
CL=33276
diff --git a/src/libmach/5obj.c b/src/libmach/5obj.c
new file mode 100644
index 0000000..034deea
--- /dev/null
+++ b/src/libmach/5obj.c
@@ -0,0 +1,171 @@
+// Inferno libmach/5obj.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/5obj.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * 5obj.c - identify and parse an arm object file
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "../cmd/5l/5.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	type;
+	char	sym;
+	char	name;
+};
+static Addr addr(Biobuf*);
+static char type2char(int);
+static void skip(Biobuf*, int);
+
+int
+_is5(char *s)
+{
+	return  s[0] == ANAME				/* ANAME */
+		&& s[1] == D_FILE			/* type */
+		&& s[2] == 1				/* sym */
+		&& s[3] == '<';				/* name of file */
+}
+
+int
+_read5(Biobuf *bp, Prog *p)
+{
+	int as, n;
+	Addr a;
+
+	as = Bgetc(bp);			/* as */
+	if(as < 0)
+		return 0;
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	else if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 6);		/* scond(1), reg(1), lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(a.type != D_OREG || a.name != D_STATIC && a.name != D_EXTERN)
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	long off;
+
+	a.type = Bgetc(bp);	/* a.type */
+	skip(bp,1);		/* reg */
+	a.sym = Bgetc(bp);	/* sym index */
+	a.name = Bgetc(bp);	/* sym type */
+	switch(a.type){
+	default:
+	case D_NONE:
+	case D_REG:
+	case D_FREG:
+	case D_PSR:
+	case D_FPCR:
+		break;
+	case D_CONST2:
+		Bgetc(bp);
+		Bgetc(bp);
+		Bgetc(bp);
+		Bgetc(bp);	// fall through
+	case D_OREG:
+	case D_CONST:
+	case D_BRANCH:
+	case D_SHIFT:
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+		if(a.sym && (a.name==D_PARAM || a.name==D_AUTO))
+			_offset(a.sym, off);
+		break;
+	case D_SCONST:
+		skip(bp, NSNAME);
+		break;
+	case D_FCONST:
+		skip(bp, 8);
+		break;
+	}
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
diff --git a/src/libmach/6.c b/src/libmach/6.c
new file mode 100644
index 0000000..0f06363
--- /dev/null
+++ b/src/libmach/6.c
@@ -0,0 +1,145 @@
+// Inferno libmach/6.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/6.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * amd64 definition
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "ureg_amd64.h"
+#include <mach.h>
+
+#define	REGOFF(x)	offsetof(struct Ureg, x)
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define FP_CTLS(x)	(REGSIZE+2*(x))
+#define FP_CTL(x)	(REGSIZE+4*(x))
+#define FP_REG(x)	(FP_CTL(8)+16*(x))
+#define XM_REG(x)	(FP_CTL(8)+8*16+16*(x))
+
+#define	FPREGSIZE	512	/* TO DO? currently only 0x1A0 used */
+
+Reglist amd64reglist[] = {
+	{"AX",		REGOFF(ax),	RINT, 'Y'},
+	{"BX",		REGOFF(bx),	RINT, 'Y'},
+	{"CX",		REGOFF(cx),	RINT, 'Y'},
+	{"DX",		REGOFF(dx),	RINT, 'Y'},
+	{"SI",		REGOFF(si),	RINT, 'Y'},
+	{"DI",		REGOFF(di),	RINT, 'Y'},
+	{"BP",		REGOFF(bp),	RINT, 'Y'},
+	{"R8",		REGOFF(r8),	RINT, 'Y'},
+	{"R9",		REGOFF(r9),	RINT, 'Y'},
+	{"R10",		REGOFF(r10),	RINT, 'Y'},
+	{"R11",		REGOFF(r11),	RINT, 'Y'},
+	{"R12",		REGOFF(r12),	RINT, 'Y'},
+	{"R13",		REGOFF(r13),	RINT, 'Y'},
+	{"R14",		REGOFF(r14),	RINT, 'Y'},
+	{"R15",		REGOFF(r15),	RINT, 'Y'},
+	{"DS",		REGOFF(ds),	RINT, 'x'},
+	{"ES",		REGOFF(es),	RINT, 'x'},
+	{"FS",		REGOFF(fs),	RINT, 'x'},
+	{"GS",		REGOFF(gs),	RINT, 'x'},
+	{"TYPE",	REGOFF(type), 	RINT, 'Y'},
+	{"TRAP",	REGOFF(type), 	RINT, 'Y'},	/* alias for acid */
+	{"ERROR",	REGOFF(error),	RINT, 'Y'},
+	{"IP",		REGOFF(ip),	RINT, 'Y'},
+	{"PC",		REGOFF(ip),	RINT, 'Y'},	/* alias for acid */
+	{"CS",		REGOFF(cs),	RINT, 'Y'},
+	{"FLAGS",	REGOFF(flags),	RINT, 'Y'},
+	{"SP",		REGOFF(sp),	RINT, 'Y'},
+	{"SS",		REGOFF(ss),	RINT, 'Y'},
+
+	{"FCW",		FP_CTLS(0),	RFLT, 'x'},
+	{"FSW",		FP_CTLS(1),	RFLT, 'x'},
+	{"FTW",		FP_CTLS(2),	RFLT, 'b'},
+	{"FOP",		FP_CTLS(3),	RFLT, 'x'},
+	{"RIP",		FP_CTL(2),	RFLT, 'Y'},
+	{"RDP",		FP_CTL(4),	RFLT, 'Y'},
+	{"MXCSR",	FP_CTL(6),	RFLT, 'X'},
+	{"MXCSRMASK",	FP_CTL(7),	RFLT, 'X'},
+	{"M0",		FP_REG(0),	RFLT, 'F'},	/* assumes double */
+	{"M1",		FP_REG(1),	RFLT, 'F'},
+	{"M2",		FP_REG(2),	RFLT, 'F'},
+	{"M3",		FP_REG(3),	RFLT, 'F'},
+	{"M4",		FP_REG(4),	RFLT, 'F'},
+	{"M5",		FP_REG(5),	RFLT, 'F'},
+	{"M6",		FP_REG(6),	RFLT, 'F'},
+	{"M7",		FP_REG(7),	RFLT, 'F'},
+	{"X0",		XM_REG(0),	RFLT, 'F'},	/* assumes double */
+	{"X1",		XM_REG(1),	RFLT, 'F'},
+	{"X2",		XM_REG(2),	RFLT, 'F'},
+	{"X3",		XM_REG(3),	RFLT, 'F'},
+	{"X4",		XM_REG(4),	RFLT, 'F'},
+	{"X5",		XM_REG(5),	RFLT, 'F'},
+	{"X6",		XM_REG(6),	RFLT, 'F'},
+	{"X7",		XM_REG(7),	RFLT, 'F'},
+	{"X8",		XM_REG(8),	RFLT, 'F'},
+	{"X9",		XM_REG(9),	RFLT, 'F'},
+	{"X10",		XM_REG(10),	RFLT, 'F'},
+	{"X11",		XM_REG(11),	RFLT, 'F'},
+	{"X12",		XM_REG(12),	RFLT, 'F'},
+	{"X13",		XM_REG(13),	RFLT, 'F'},
+	{"X14",		XM_REG(14),	RFLT, 'F'},
+	{"X15",		XM_REG(15),	RFLT, 'F'},
+	{"X16",		XM_REG(16),	RFLT, 'F'},
+/*
+	{"F0",		FP_REG(7),	RFLT, '3'},
+	{"F1",		FP_REG(6),	RFLT, '3'},
+	{"F2",		FP_REG(5),	RFLT, '3'},
+	{"F3",		FP_REG(4),	RFLT, '3'},
+	{"F4",		FP_REG(3),	RFLT, '3'},
+	{"F5",		FP_REG(2),	RFLT, '3'},
+	{"F6",		FP_REG(1),	RFLT, '3'},
+	{"F7",		FP_REG(0),	RFLT, '3'},
+*/
+	{  0 }
+};
+
+Mach mamd64=
+{
+	"amd64",
+	MAMD64,			/* machine type */
+	amd64reglist,		/* register list */
+	REGSIZE,		/* size of registers in bytes */
+	FPREGSIZE,		/* size of fp registers in bytes */
+	"PC",			/* name of PC */
+	"SP",			/* name of SP */
+	0,			/* link register */
+	"setSB",		/* static base register name (bogus anyways) */
+	0,			/* static base register value */
+	0x1000,			/* page size */
+	0xFFFFFFFF80110000ULL,	/* kernel base */
+	0xFFFF800000000000ULL,	/* kernel text mask */
+	0x00007FFFFFFFF000ULL,	/* user stack top */
+	1,			/* quantization of pc */
+	8,			/* szaddr */
+	4,			/* szreg */
+	4,			/* szfloat */
+	8,			/* szdouble */
+};
diff --git a/src/libmach/6obj.c b/src/libmach/6obj.c
new file mode 100644
index 0000000..578ae2b
--- /dev/null
+++ b/src/libmach/6obj.c
@@ -0,0 +1,179 @@
+// Inferno libmach/6obj.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/6obj.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * 6obj.c - identify and parse an amd64 object file
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "../cmd/6l/6.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	sym;
+	char	flags;
+	char gotype;
+};
+static	Addr	addr(Biobuf*);
+static	char	type2char(int);
+static	void	skip(Biobuf*, int);
+
+int
+_is6(char *t)
+{
+	uchar *s = (uchar*)t;
+
+	return  s[0] == (ANAME&0xff)			/* aslo = ANAME */
+		&& s[1] == ((ANAME>>8)&0xff)
+		&& s[2] == D_FILE			/* type */
+		&& s[3] == 1				/* sym */
+		&& s[4] == '<';				/* name of file */
+}
+
+int
+_read6(Biobuf *bp, Prog* p)
+{
+	int as, n, c;
+	Addr a;
+
+	as = Bgetc(bp);		/* as(low) */
+	if(as < 0)
+		return 0;
+	c = Bgetc(bp);		/* as(high) */
+	if(c < 0)
+		return 0;
+	as |= ((c & 0xff) << 8);
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 4);		/* lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(!(a.flags & T_SYM))
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	int t;
+	int32 l;
+	vlong off;
+
+	off = 0;
+	a.sym = -1;
+	a.flags = Bgetc(bp);			/* flags */
+	a.gotype = 0;
+	if(a.flags & T_INDEX)
+		skip(bp, 2);
+	if(a.flags & T_OFFSET){
+		l = Bgetc(bp);
+		l |= Bgetc(bp) << 8;
+		l |= Bgetc(bp) << 16;
+		l |= Bgetc(bp) << 24;
+		off = l;
+		if(a.flags & T_64){
+			l = Bgetc(bp);
+			l |= Bgetc(bp) << 8;
+			l |= Bgetc(bp) << 16;
+			l |= Bgetc(bp) << 24;
+			off = ((vlong)l << 32) | (off & 0xFFFFFFFF);
+		}
+		if(off < 0)
+			off = -off;
+	}
+	if(a.flags & T_SYM)
+		a.sym = Bgetc(bp);
+	if(a.flags & T_FCONST)
+		skip(bp, 8);
+	else
+	if(a.flags & T_SCONST)
+		skip(bp, NSNAME);
+	if(a.flags & T_TYPE) {
+		t = Bgetc(bp);
+		if(a.sym > 0 && (t==D_PARAM || t==D_AUTO))
+			_offset(a.sym, off);
+	}
+	if(a.flags & T_GOTYPE)
+		a.gotype = Bgetc(bp);
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
diff --git a/src/libmach/8.c b/src/libmach/8.c
new file mode 100644
index 0000000..07ec305
--- /dev/null
+++ b/src/libmach/8.c
@@ -0,0 +1,107 @@
+// Inferno libmach/8.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * 386 definition
+ */
+#include <u.h>
+#include <bio.h>
+#include <ureg_x86.h>
+#include <mach.h>
+
+#define	REGOFF(x)	(uintptr)(&((struct Ureg *) 0)->x)
+
+#define PC		REGOFF(pc)
+#define SP		REGOFF(sp)
+#define	AX		REGOFF(ax)
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define FP_CTL(x)	(REGSIZE+4*(x))
+#define FP_REG(x)	(FP_CTL(7)+10*(x))
+#define	FPREGSIZE	(7*4+8*10)
+
+Reglist i386reglist[] = {
+	{"DI",		REGOFF(di),	RINT, 'X'},
+	{"SI",		REGOFF(si),	RINT, 'X'},
+	{"BP",		REGOFF(bp),	RINT, 'X'},
+	{"BX",		REGOFF(bx),	RINT, 'X'},
+	{"DX",		REGOFF(dx),	RINT, 'X'},
+	{"CX",		REGOFF(cx),	RINT, 'X'},
+	{"AX",		REGOFF(ax),	RINT, 'X'},
+	{"GS",		REGOFF(gs),	RINT, 'X'},
+	{"FS",		REGOFF(fs),	RINT, 'X'},
+	{"ES",		REGOFF(es),	RINT, 'X'},
+	{"DS",		REGOFF(ds),	RINT, 'X'},
+	{"TRAP",	REGOFF(trap), 	RINT, 'X'},
+	{"ECODE",	REGOFF(ecode),	RINT, 'X'},
+	{"PC",		PC,		RINT, 'X'},
+	{"CS",		REGOFF(cs),	RINT, 'X'},
+	{"EFLAGS",	REGOFF(flags),	RINT, 'X'},
+	{"SP",		SP,		RINT, 'X'},
+	{"SS",		REGOFF(ss),	RINT, 'X'},
+
+	{"E0",		FP_CTL(0),	RFLT, 'X'},
+	{"E1",		FP_CTL(1),	RFLT, 'X'},
+	{"E2",		FP_CTL(2),	RFLT, 'X'},
+	{"E3",		FP_CTL(3),	RFLT, 'X'},
+	{"E4",		FP_CTL(4),	RFLT, 'X'},
+	{"E5",		FP_CTL(5),	RFLT, 'X'},
+	{"E6",		FP_CTL(6),	RFLT, 'X'},
+	{"F0",		FP_REG(0),	RFLT, '3'},
+	{"F1",		FP_REG(1),	RFLT, '3'},
+	{"F2",		FP_REG(2),	RFLT, '3'},
+	{"F3",		FP_REG(3),	RFLT, '3'},
+	{"F4",		FP_REG(4),	RFLT, '3'},
+	{"F5",		FP_REG(5),	RFLT, '3'},
+	{"F6",		FP_REG(6),	RFLT, '3'},
+	{"F7",		FP_REG(7),	RFLT, '3'},
+	{  0 }
+};
+
+Mach mi386 =
+{
+	"386",
+	MI386,		/* machine type */
+	i386reglist,	/* register list */
+	REGSIZE,	/* size of registers in bytes */
+	FPREGSIZE,	/* size of fp registers in bytes */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	0,		/* link register */
+	"setSB",	/* static base register name (bogus anyways) */
+	0,		/* static base register value */
+	0x1000,		/* page size */
+	0x80100000ULL,	/* kernel base */
+	0xF0000000ULL,	/* kernel text mask */
+	0xFFFFFFFFULL,	/* user stack top */
+	1,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+};
diff --git a/src/libmach/8db.c b/src/libmach/8db.c
new file mode 100644
index 0000000..0abf77b
--- /dev/null
+++ b/src/libmach/8db.c
@@ -0,0 +1,2331 @@
+// Inferno libmach/8db.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8db.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#define Ureg UregAmd64
+#include <ureg_amd64.h>
+#undef Ureg
+#define Ureg Ureg386
+#include <ureg_x86.h>
+#undef Ureg
+
+typedef struct UregAmd64 UregAmd64;
+typedef struct Ureg386 Ureg386;
+
+/*
+ * i386-specific debugger interface
+ * also amd64 extensions
+ */
+
+static	char	*i386excep(Map*, Rgetter);
+
+static	int	i386trace(Map*, uvlong, uvlong, uvlong, Tracer);
+static	uvlong	i386frame(Map*, uvlong, uvlong, uvlong, uvlong);
+static	int	i386foll(Map*, uvlong, Rgetter, uvlong*);
+static	int	i386inst(Map*, uvlong, char, char*, int);
+static	int	i386das(Map*, uvlong, char*, int);
+static	int	i386instlen(Map*, uvlong);
+
+static	char	STARTSYM[] =	"_main";
+static	char	GOSTARTSYM[] =	"sys·goexit";
+static	char	PROFSYM[] =	"_mainp";
+static	char	FRAMENAME[] =	".frame";
+static	char	LESSSTACK[] = "sys·lessstack";
+static	char	MORESTACK[] = "sys·morestack";
+static char *excname[] =
+{
+[0]	"divide error",
+[1]	"debug exception",
+[4]	"overflow",
+[5]	"bounds check",
+[6]	"invalid opcode",
+[7]	"math coprocessor emulation",
+[8]	"double fault",
+[9]	"math coprocessor overrun",
+[10]	"invalid TSS",
+[11]	"segment not present",
+[12]	"stack exception",
+[13]	"general protection violation",
+[14]	"page fault",
+[16]	"math coprocessor error",
+[17]	"alignment check",
+[18]	"machine check",
+[19]	"floating-point exception",
+[24]	"clock",
+[25]	"keyboard",
+[27]	"modem status",
+[28]	"serial line status",
+[30]	"floppy disk",
+[36]	"mouse",
+[37]	"math coprocessor",
+[38]	"hard disk",
+[64]	"system call",
+};
+
+Machdata i386mach =
+{
+	{0xCC, 0, 0, 0},	/* break point: INT 3 */
+	1,			/* break point size */
+
+	leswab,			/* convert short to local byte order */
+	leswal,			/* convert int32 to local byte order */
+	leswav,			/* convert vlong to local byte order */
+	i386trace,		/* C traceback */
+	i386frame,		/* frame finder */
+	i386excep,		/* print exception */
+	0,			/* breakpoint fixup */
+	leieeesftos,		/* single precision float printer */
+	leieeedftos,		/* double precision float printer */
+	i386foll,		/* following addresses */
+	i386inst,		/* print instruction */
+	i386das,		/* dissembler */
+	i386instlen,		/* instruction size calculation */
+};
+
+static char*
+i386excep(Map *map, Rgetter rget)
+{
+	uint32 c;
+	uvlong pc;
+	static char buf[16];
+
+	c = (*rget)(map, "TRAP");
+	if(c > 64 || excname[c] == 0) {
+		if (c == 3) {
+			pc = (*rget)(map, "PC");
+			if (get1(map, pc, (uchar*)buf, machdata->bpsize) > 0)
+			if (memcmp(buf, machdata->bpinst, machdata->bpsize) == 0)
+				return "breakpoint";
+		}
+		snprint(buf, sizeof(buf), "exception %ld", c);
+		return buf;
+	} else
+		return excname[c];
+}
+
+static int
+i386trace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
+{
+	int i;
+	uvlong osp, pc1;
+	Symbol s, f, s1;
+	extern Mach mamd64;
+	int isamd64;
+	uvlong g, m, lessstack, morestack, stktop;
+
+	isamd64 = (mach == &mamd64);
+
+	// ../pkg/runtime/runtime.h
+	// G is
+	//	byte* stackguard
+	//	byte* stackbase (= Stktop*)
+	//	Defer* defer
+	//	Gobuf sched
+	// TODO(rsc): Need some way to get at the g for other threads.
+	// Probably need to pass it into the trace function.
+	g = 0;
+	if(isamd64)
+		geta(map, offsetof(struct UregAmd64, r15), &g);
+	else {
+		// TODO(rsc): How to fetch g on 386?
+	}
+	stktop = 0;
+	if(g != 0)
+		geta(map, g+1*mach->szaddr, &stktop);
+
+	lessstack = 0;
+	if(lookup(0, LESSSTACK, &s))
+		lessstack = s.value;
+	morestack = 0;
+	if(lookup(0, MORESTACK, &s))
+		morestack = s.value;
+
+	USED(link);
+	osp = 0;
+	i = 0;
+
+	for(;;) {
+		if(!findsym(pc, CTEXT, &s)) {
+			// check for closure return sequence
+			uchar buf[8], *p;
+			if(get1(map, pc, buf, 8) < 0)
+				break;
+			// ADDQ $xxx, SP; RET
+			p = buf;
+			if(mach == &mamd64) {
+				if(p[0] != 0x48)
+					break;
+				p++;
+			}
+			if(p[0] != 0x81 || p[1] != 0xc4 || p[6] != 0xc3)
+				break;
+			sp += p[2] | (p[3]<<8) | (p[4]<<16) | (p[5]<<24);
+			if(geta(map, sp, &pc) < 0)
+				break;
+			sp += mach->szaddr;
+			continue;
+		}
+
+		if (osp == sp)
+			break;
+		osp = sp;
+
+		if(strcmp(STARTSYM, s.name) == 0 ||
+		   strcmp(GOSTARTSYM, s.name) == 0 ||
+		   strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(s.value == morestack) {
+			// In the middle of morestack.
+			// Caller is m->morepc.
+			// Caller's caller is in m->morearg.
+			// TODO(rsc): 386
+			geta(map, offsetof(struct UregAmd64, r14), &m);
+
+			pc = 0;
+			sp = 0;
+			pc1 = 0;
+			s1 = s;
+			memset(&s, 0, sizeof s);
+			geta(map, m+1*mach->szaddr, &pc1);	// m->morepc
+			geta(map, m+2*mach->szaddr, &sp);	// m->morebuf.sp
+			geta(map, m+3*mach->szaddr, &pc);	// m->morebuf.pc
+			findsym(pc1, CTEXT, &s);
+			(*trace)(map, pc1, sp-mach->szaddr, &s1);	// morestack symbol; caller's PC/SP
+
+			// caller's caller
+			s1 = s;
+			findsym(pc, CTEXT, &s);
+			(*trace)(map, pc, sp, &s1);		// morestack's caller; caller's caller's PC/SP
+			continue;
+		}
+
+		if(pc == lessstack) {
+			// ../pkg/runtime/runtime.h
+			// Stktop is
+			//	byte* stackguard
+			//	byte* stackbase
+			//	Gobuf gobuf
+			//		byte* sp;
+			//		byte* pc;
+			//		G*	g;
+			if(!isamd64)
+				fprint(2, "warning: cannot unwind stack split on 386\n");
+			if(stktop == 0)
+				break;
+			pc = 0;
+			sp = 0;
+			geta(map, stktop+2*mach->szaddr, &sp);
+			geta(map, stktop+3*mach->szaddr, &pc);
+			geta(map, stktop+1*mach->szaddr, &stktop);
+			(*trace)(map, pc, sp, &s1);
+			continue;
+		}
+
+		s1 = s;
+		pc1 = 0;
+		if(pc != s.value) {	/* not at first instruction */
+			if(findlocal(&s, FRAMENAME, &f) == 0)
+				break;
+			geta(map, sp, &pc1);
+			sp += f.value-mach->szaddr;
+		}
+		if(geta(map, sp, &pc) < 0)
+			break;
+
+		// If PC is not valid, assume we caught the function
+		// before it moved the stack pointer down or perhaps
+		// after it moved the stack pointer back up.
+		// Try the PC we'd have gotten without the stack
+		// pointer adjustment above (pc != s.value).
+		// This only matters for the first frame, and it is only
+		// a heuristic, but it does help.
+		if(!findsym(pc, CTEXT, &s) || strcmp(s.name, "etext") == 0)
+			pc = pc1;
+
+		if(pc == 0)
+			break;
+
+		if(pc != lessstack)
+			(*trace)(map, pc, sp, &s1);
+		sp += mach->szaddr;
+
+		if(++i > 1000)
+			break;
+	}
+	return i;
+}
+
+static uvlong
+i386frame(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
+{
+	Symbol s, f;
+
+	USED(link);
+	while (findsym(pc, CTEXT, &s)) {
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(pc != s.value) {	/* not first instruction */
+			if(findlocal(&s, FRAMENAME, &f) == 0)
+				break;
+			sp += f.value-mach->szaddr;
+		}
+
+		if (s.value == addr)
+			return sp;
+
+		if (geta(map, sp, &pc) < 0)
+			break;
+		sp += mach->szaddr;
+	}
+	return 0;
+}
+
+	/* I386/486 - Disassembler and related functions */
+
+/*
+ *  an instruction
+ */
+typedef struct Instr Instr;
+struct	Instr
+{
+	uchar	mem[1+1+1+1+2+1+1+4+4];		/* raw instruction */
+	uvlong	addr;		/* address of start of instruction */
+	int	n;		/* number of bytes in instruction */
+	char	*prefix;	/* instr prefix */
+	char	*segment;	/* segment override */
+	uchar	jumptype;	/* set to the operand type for jump/ret/call */
+	uchar	amd64;
+	uchar	rex;		/* REX prefix (or zero) */
+	char	osize;		/* 'W' or 'L' (or 'Q' on amd64) */
+	char	asize;		/* address size 'W' or 'L' (or 'Q' or amd64) */
+	uchar	mod;		/* bits 6-7 of mod r/m field */
+	uchar	reg;		/* bits 3-5 of mod r/m field */
+	char	ss;		/* bits 6-7 of SIB */
+	char	index;		/* bits 3-5 of SIB */
+	char	base;		/* bits 0-2 of SIB */
+	char	rip;		/* RIP-relative in amd64 mode */
+	uchar	opre;		/* f2/f3 could introduce media */
+	short	seg;		/* segment of far address */
+	uint32	disp;		/* displacement */
+	uint32 	imm;		/* immediate */
+	uint32 	imm2;		/* second immediate operand */
+	uvlong	imm64;		/* big immediate */
+	char	*curr;		/* fill level in output buffer */
+	char	*end;		/* end of output buffer */
+	char	*err;		/* error message */
+};
+
+	/* 386 register (ha!) set */
+enum{
+	AX=0,
+	CX,
+	DX,
+	BX,
+	SP,
+	BP,
+	SI,
+	DI,
+
+	/* amd64 */
+	R8,
+	R9,
+	R10,
+	R11,
+	R12,
+	R13,
+	R14,
+	R15
+};
+
+	/* amd64 rex extension byte */
+enum{
+	REXW		= 1<<3,	/* =1, 64-bit operand size */
+	REXR		= 1<<2,	/* extend modrm reg */
+	REXX		= 1<<1,	/* extend sib index */
+	REXB		= 1<<0	/* extend modrm r/m, sib base, or opcode reg */
+};
+
+	/* Operand Format codes */
+/*
+%A	-	address size register modifier (!asize -> 'E')
+%C	-	Control register CR0/CR1/CR2
+%D	-	Debug register DR0/DR1/DR2/DR3/DR6/DR7
+%I	-	second immediate operand
+%O	-	Operand size register modifier (!osize -> 'E')
+%T	-	Test register TR6/TR7
+%S	-	size code ('W' or 'L')
+%W	-	Weird opcode: OSIZE == 'W' => "CBW"; else => "CWDE"
+%d	-	displacement 16-32 bits
+%e	-	effective address - Mod R/M value
+%f	-	floating point register F0-F7 - from Mod R/M register
+%g	-	segment register
+%i	-	immediate operand 8-32 bits
+%p	-	PC-relative - signed displacement in immediate field
+%r	-	Reg from Mod R/M
+%w	-	Weird opcode: OSIZE == 'W' => "CWD"; else => "CDQ"
+*/
+
+typedef struct Optable Optable;
+struct Optable
+{
+	char	operand[2];
+	void	*proto;		/* actually either (char*) or (Optable*) */
+};
+	/* Operand decoding codes */
+enum {
+	Ib = 1,			/* 8-bit immediate - (no sign extension)*/
+	Ibs,			/* 8-bit immediate (sign extended) */
+	Jbs,			/* 8-bit sign-extended immediate in jump or call */
+	Iw,			/* 16-bit immediate -> imm */
+	Iw2,			/* 16-bit immediate -> imm2 */
+	Iwd,			/* Operand-sized immediate (no sign extension)*/
+	Iwdq,			/* Operand-sized immediate, possibly 64 bits */
+	Awd,			/* Address offset */
+	Iwds,			/* Operand-sized immediate (sign extended) */
+	RM,			/* Word or int32 R/M field with register (/r) */
+	RMB,			/* Byte R/M field with register (/r) */
+	RMOP,			/* Word or int32 R/M field with op code (/digit) */
+	RMOPB,			/* Byte R/M field with op code (/digit) */
+	RMR,			/* R/M register only (mod = 11) */
+	RMM,			/* R/M memory only (mod = 0/1/2) */
+	R0,			/* Base reg of Mod R/M is literal 0x00 */
+	R1,			/* Base reg of Mod R/M is literal 0x01 */
+	FRMOP,			/* Floating point R/M field with opcode */
+	FRMEX,			/* Extended floating point R/M field with opcode */
+	JUMP,			/* Jump or Call flag - no operand */
+	RET,			/* Return flag - no operand */
+	OA,			/* literal 0x0a byte */
+	PTR,			/* Seg:Displacement addr (ptr16:16 or ptr16:32) */
+	AUX,			/* Multi-byte op code - Auxiliary table */
+	AUXMM,			/* multi-byte op code - auxiliary table chosen by prefix */
+	PRE,			/* Instr Prefix */
+	OPRE,			/* Instr Prefix or media op extension */
+	SEG,			/* Segment Prefix */
+	OPOVER,			/* Operand size override */
+	ADDOVER,		/* Address size override */
+};
+
+static Optable optab0F00[8]=
+{
+[0x00]	0,0,		"MOVW	LDT,%e",
+[0x01]	0,0,		"MOVW	TR,%e",
+[0x02]	0,0,		"MOVW	%e,LDT",
+[0x03]	0,0,		"MOVW	%e,TR",
+[0x04]	0,0,		"VERR	%e",
+[0x05]	0,0,		"VERW	%e",
+};
+
+static Optable optab0F01[8]=
+{
+[0x00]	0,0,		"MOVL	GDTR,%e",
+[0x01]	0,0,		"MOVL	IDTR,%e",
+[0x02]	0,0,		"MOVL	%e,GDTR",
+[0x03]	0,0,		"MOVL	%e,IDTR",
+[0x04]	0,0,		"MOVW	MSW,%e",	/* word */
+[0x06]	0,0,		"MOVW	%e,MSW",	/* word */
+[0x07]	0,0,		"INVLPG	%e",		/* or SWAPGS */
+};
+
+static Optable optab0F01F8[1]=
+{
+[0x00]	0,0,		"SWAPGS",
+};
+
+/* 0F71 */
+/* 0F72 */
+/* 0F73 */
+
+static Optable optab0FAE[8]=
+{
+[0x00]	0,0,		"FXSAVE	%e",
+[0x01]	0,0,		"FXRSTOR	%e",
+[0x02]	0,0,		"LDMXCSR	%e",
+[0x03]	0,0,		"STMXCSR	%e",
+[0x05]	0,0,		"LFENCE",
+[0x06]	0,0,		"MFENCE",
+[0x07]	0,0,		"SFENCE",
+};
+
+/* 0F18 */
+/* 0F0D */
+
+static Optable optab0FBA[8]=
+{
+[0x04]	Ib,0,		"BT%S	%i,%e",
+[0x05]	Ib,0,		"BTS%S	%i,%e",
+[0x06]	Ib,0,		"BTR%S	%i,%e",
+[0x07]	Ib,0,		"BTC%S	%i,%e",
+};
+
+static Optable optab0F0F[256]=
+{
+[0x0c]	0,0,		"PI2FW	%m,%M",
+[0x0d]	0,0,		"PI2L	%m,%M",
+[0x1c]	0,0,		"PF2IW	%m,%M",
+[0x1d]	0,0,		"PF2IL	%m,%M",
+[0x8a]	0,0,		"PFNACC	%m,%M",
+[0x8e]	0,0,		"PFPNACC	%m,%M",
+[0x90]	0,0,		"PFCMPGE	%m,%M",
+[0x94]	0,0,		"PFMIN	%m,%M",
+[0x96]	0,0,		"PFRCP	%m,%M",
+[0x97]	0,0,		"PFRSQRT	%m,%M",
+[0x9a]	0,0,		"PFSUB	%m,%M",
+[0x9e]	0,0,		"PFADD	%m,%M",
+[0xa0]	0,0,		"PFCMPGT	%m,%M",
+[0xa4]	0,0,		"PFMAX	%m,%M",
+[0xa6]	0,0,		"PFRCPIT1	%m,%M",
+[0xa7]	0,0,		"PFRSQIT1	%m,%M",
+[0xaa]	0,0,		"PFSUBR	%m,%M",
+[0xae]	0,0,		"PFACC	%m,%M",
+[0xb0]	0,0,		"PFCMPEQ	%m,%M",
+[0xb4]	0,0,		"PFMUL	%m,%M",
+[0xb6]	0,0,		"PFRCPI2T	%m,%M",
+[0xb7]	0,0,		"PMULHRW	%m,%M",
+[0xbb]	0,0,		"PSWAPL	%m,%M",
+};
+
+static Optable optab0FC7[8]=
+{
+[0x01]	0,0,		"CMPXCHG8B	%e",
+};
+
+static Optable optab660F71[8]=
+{
+[0x02]	Ib,0,		"PSRLW	%i,%X",
+[0x04]	Ib,0,		"PSRAW	%i,%X",
+[0x06]	Ib,0,		"PSLLW	%i,%X",
+};
+
+static Optable optab660F72[8]=
+{
+[0x02]	Ib,0,		"PSRLL	%i,%X",
+[0x04]	Ib,0,		"PSRAL	%i,%X",
+[0x06]	Ib,0,		"PSLLL	%i,%X",
+};
+
+static Optable optab660F73[8]=
+{
+[0x02]	Ib,0,		"PSRLQ	%i,%X",
+[0x03]	Ib,0,		"PSRLO	%i,%X",
+[0x06]	Ib,0,		"PSLLQ	%i,%X",
+[0x07]	Ib,0,		"PSLLO	%i,%X",
+};
+
+static Optable optab660F[256]=
+{
+[0x2B]	RM,0,		"MOVNTPD	%x,%e",
+[0x2E]	RM,0,		"UCOMISD	%x,%X",
+[0x2F]	RM,0,		"COMISD	%x,%X",
+[0x5A]	RM,0,		"CVTPD2PS	%x,%X",
+[0x5B]	RM,0,		"CVTPS2PL	%x,%X",
+[0x6A]	RM,0,		"PUNPCKHLQ %x,%X",
+[0x6B]	RM,0,		"PACKSSLW %x,%X",
+[0x6C]	RM,0,		"PUNPCKLQDQ %x,%X",
+[0x6D]	RM,0,		"PUNPCKHQDQ %x,%X",
+[0x6E]	RM,0,		"MOV%S	%e,%X",
+[0x6F]	RM,0,		"MOVO	%x,%X",		/* MOVDQA */
+[0x70]	RM,Ib,		"PSHUFL	%i,%x,%X",
+[0x71]	RMOP,0,		optab660F71,
+[0x72]	RMOP,0,		optab660F72,
+[0x73]	RMOP,0,		optab660F73,
+[0x7E]	RM,0,		"MOV%S	%X,%e",
+[0x7F]	RM,0,		"MOVO	%X,%x",
+[0xC4]	RM,Ib,		"PINSRW	%i,%e,%X",
+[0xC5]	RMR,Ib,		"PEXTRW	%i,%X,%e",
+[0xD4]	RM,0,		"PADDQ	%x,%X",
+[0xD5]	RM,0,		"PMULLW	%x,%X",
+[0xD6]	RM,0,		"MOVQ	%X,%x",
+[0xE6]	RM,0,		"CVTTPD2PL	%x,%X",
+[0xE7]	RM,0,		"MOVNTO	%X,%e",
+[0xF7]	RM,0,		"MASKMOVOU	%x,%X",
+};
+
+static Optable optabF20F[256]=
+{
+[0x10]	RM,0,		"MOVSD	%x,%X",
+[0x11]	RM,0,		"MOVSD	%X,%x",
+[0x2A]	RM,0,		"CVTS%S2SD	%e,%X",
+[0x2C]	RM,0,		"CVTTSD2S%S	%x,%r",
+[0x2D]	RM,0,		"CVTSD2S%S	%x,%r",
+[0x5A]	RM,0,		"CVTSD2SS	%x,%X",
+[0x6F]	RM,0,		"MOVOU	%x,%X",
+[0x70]	RM,Ib,		"PSHUFLW	%i,%x,%X",
+[0x7F]	RM,0,		"MOVOU	%X,%x",
+[0xD6]	RM,0,		"MOVQOZX	%M,%X",
+[0xE6]	RM,0,		"CVTPD2PL	%x,%X",
+};
+
+static Optable optabF30F[256]=
+{
+[0x10]	RM,0,		"MOVSS	%x,%X",
+[0x11]	RM,0,		"MOVSS	%X,%x",
+[0x2A]	RM,0,		"CVTS%S2SS	%e,%X",
+[0x2C]	RM,0,		"CVTTSS2S%S	%x,%r",
+[0x2D]	RM,0,		"CVTSS2S%S	%x,%r",
+[0x5A]	RM,0,		"CVTSS2SD	%x,%X",
+[0x5B]	RM,0,		"CVTTPS2PL	%x,%X",
+[0x6F]	RM,0,		"MOVOU	%x,%X",
+[0x70]	RM,Ib,		"PSHUFHW	%i,%x,%X",
+[0x7E]	RM,0,		"MOVQOZX	%x,%X",
+[0x7F]	RM,0,		"MOVOU	%X,%x",
+[0xD6]	RM,0,		"MOVQOZX	%m*,%X",
+[0xE6]	RM,0,		"CVTPL2PD	%x,%X",
+};
+
+static Optable optab0F[256]=
+{
+[0x00]	RMOP,0,		optab0F00,
+[0x01]	RMOP,0,		optab0F01,
+[0x02]	RM,0,		"LAR	%e,%r",
+[0x03]	RM,0,		"LSL	%e,%r",
+[0x05]	0,0,		"SYSCALL",
+[0x06]	0,0,		"CLTS",
+[0x07]	0,0,		"SYSRET",
+[0x08]	0,0,		"INVD",
+[0x09]	0,0,		"WBINVD",
+[0x0B]	0,0,		"UD2",
+[0x0F]	RM,AUX,		optab0F0F,		/* 3DNow! */
+[0x10]	RM,0,		"MOVU%s	%x,%X",
+[0x11]	RM,0,		"MOVU%s	%X,%x",
+[0x12]	RM,0,		"MOV[H]L%s	%x,%X",	/* TO DO: H if source is XMM */
+[0x13]	RM,0,		"MOVL%s	%X,%e",
+[0x14]	RM,0,		"UNPCKL%s	%x,%X",
+[0x15]	RM,0,		"UNPCKH%s	%x,%X",
+[0x16]	RM,0,		"MOV[L]H%s	%x,%X",	/* TO DO: L if source is XMM */
+[0x17]	RM,0,		"MOVH%s	%X,%x",
+[0x20]	RMR,0,		"MOVL	%C,%e",
+[0x21]	RMR,0,		"MOVL	%D,%e",
+[0x22]	RMR,0,		"MOVL	%e,%C",
+[0x23]	RMR,0,		"MOVL	%e,%D",
+[0x24]	RMR,0,		"MOVL	%T,%e",
+[0x26]	RMR,0,		"MOVL	%e,%T",
+[0x28]	RM,0,		"MOVA%s	%x,%X",
+[0x29]	RM,0,		"MOVA%s	%X,%x",
+[0x2A]	RM,0,		"CVTPL2%s	%m*,%X",
+[0x2B]	RM,0,		"MOVNT%s	%X,%e",
+[0x2C]	RM,0,		"CVTT%s2PL	%x,%M",
+[0x2D]	RM,0,		"CVT%s2PL	%x,%M",
+[0x2E]	RM,0,		"UCOMISS	%x,%X",
+[0x2F]	RM,0,		"COMISS	%x,%X",
+[0x30]	0,0,		"WRMSR",
+[0x31]	0,0,		"RDTSC",
+[0x32]	0,0,		"RDMSR",
+[0x33]	0,0,		"RDPMC",
+[0x42]	RM,0,		"CMOVC	%e,%r",		/* CF */
+[0x43]	RM,0,		"CMOVNC	%e,%r",		/* ¬ CF */
+[0x44]	RM,0,		"CMOVZ	%e,%r",		/* ZF */
+[0x45]	RM,0,		"CMOVNZ	%e,%r",		/* ¬ ZF */
+[0x46]	RM,0,		"CMOVBE	%e,%r",		/* CF ∨ ZF */
+[0x47]	RM,0,		"CMOVA	%e,%r",		/* ¬CF ∧ ¬ZF */
+[0x48]	RM,0,		"CMOVS	%e,%r",		/* SF */
+[0x49]	RM,0,		"CMOVNS	%e,%r",		/* ¬ SF */
+[0x4A]	RM,0,		"CMOVP	%e,%r",		/* PF */
+[0x4B]	RM,0,		"CMOVNP	%e,%r",		/* ¬ PF */
+[0x4C]	RM,0,		"CMOVLT	%e,%r",		/* LT ≡ OF ≠ SF */
+[0x4D]	RM,0,		"CMOVGE	%e,%r",		/* GE ≡ ZF ∨ SF */
+[0x4E]	RM,0,		"CMOVLE	%e,%r",		/* LE ≡ ZF ∨ LT */
+[0x4F]	RM,0,		"CMOVGT	%e,%r",		/* GT ≡ ¬ZF ∧ GE */
+[0x50]	RM,0,		"MOVMSK%s	%X,%r",	/* TO DO: check */
+[0x51]	RM,0,		"SQRT%s	%x,%X",
+[0x52]	RM,0,		"RSQRT%s	%x,%X",
+[0x53]	RM,0,		"RCP%s	%x,%X",
+[0x54]	RM,0,		"AND%s	%x,%X",
+[0x55]	RM,0,		"ANDN%s	%x,%X",
+[0x56]	RM,0,		"OR%s	%x,%X",		/* TO DO: S/D */
+[0x57]	RM,0,		"XOR%s	%x,%X",		/* S/D */
+[0x58]	RM,0,		"ADD%s	%x,%X",		/* S/P S/D */
+[0x59]	RM,0,		"MUL%s	%x,%X",
+[0x5A]	RM,0,		"CVTPS2PD	%x,%X",
+[0x5B]	RM,0,		"CVTPL2PS	%x,%X",
+[0x5C]	RM,0,		"SUB%s	%x,%X",
+[0x5D]	RM,0,		"MIN%s	%x,%X",
+[0x5E]	RM,0,		"DIV%s	%x,%X",		/* TO DO: S/P S/D */
+[0x5F]	RM,0,		"MAX%s	%x,%X",
+[0x60]	RM,0,		"PUNPCKLBW %m,%M",
+[0x61]	RM,0,		"PUNPCKLWL %m,%M",
+[0x62]	RM,0,		"PUNPCKLLQ %m,%M",
+[0x63]	RM,0,		"PACKSSWB %m,%M",
+[0x64]	RM,0,		"PCMPGTB %m,%M",
+[0x65]	RM,0,		"PCMPGTW %m,%M",
+[0x66]	RM,0,		"PCMPGTL %m,%M",
+[0x67]	RM,0,		"PACKUSWB %m,%M",
+[0x68]	RM,0,		"PUNPCKHBW %m,%M",
+[0x69]	RM,0,		"PUNPCKHWL %m,%M",
+[0x6A]	RM,0,		"PUNPCKHLQ %m,%M",
+[0x6B]	RM,0,		"PACKSSLW %m,%M",
+[0x6E]	RM,0,		"MOV%S %e,%M",
+[0x6F]	RM,0,		"MOVQ %m,%M",
+[0x70]	RM,Ib,		"PSHUFW	%i,%m,%M",
+[0x74]	RM,0,		"PCMPEQB %m,%M",
+[0x75]	RM,0,		"PCMPEQW %m,%M",
+[0x76]	RM,0,		"PCMPEQL %m,%M",
+[0x7E]	RM,0,		"MOV%S %M,%e",
+[0x7F]	RM,0,		"MOVQ %M,%m",
+[0xAE]	RMOP,0,		optab0FAE,
+[0xAA]	0,0,		"RSM",
+[0xB0]	RM,0,		"CMPXCHGB	%r,%e",
+[0xB1]	RM,0,		"CMPXCHG%S	%r,%e",
+[0xC0]	RMB,0,		"XADDB	%r,%e",
+[0xC1]	RM,0,		"XADD%S	%r,%e",
+[0xC2]	RM,Ib,		"CMP%s	%i,%x,%X",
+[0xC3]	RM,0,		"MOVNTI%S	%r,%e",
+[0xC6]	RM,Ib,		"SHUF%s	%i,%x,%X",
+[0xC8]	0,0,		"BSWAP	AX",
+[0xC9]	0,0,		"BSWAP	CX",
+[0xCA]	0,0,		"BSWAP	DX",
+[0xCB]	0,0,		"BSWAP	BX",
+[0xCC]	0,0,		"BSWAP	SP",
+[0xCD]	0,0,		"BSWAP	BP",
+[0xCE]	0,0,		"BSWAP	SI",
+[0xCF]	0,0,		"BSWAP	DI",
+[0xD1]	RM,0,		"PSRLW %m,%M",
+[0xD2]	RM,0,		"PSRLL %m,%M",
+[0xD3]	RM,0,		"PSRLQ %m,%M",
+[0xD5]	RM,0,		"PMULLW %m,%M",
+[0xD6]	RM,0,		"MOVQOZX	%m*,%X",
+[0xD7]	RM,0,		"PMOVMSKB %m,%r",
+[0xD8]	RM,0,		"PSUBUSB %m,%M",
+[0xD9]	RM,0,		"PSUBUSW %m,%M",
+[0xDA]	RM,0,		"PMINUB %m,%M",
+[0xDB]	RM,0,		"PAND %m,%M",
+[0xDC]	RM,0,		"PADDUSB %m,%M",
+[0xDD]	RM,0,		"PADDUSW %m,%M",
+[0xDE]	RM,0,		"PMAXUB %m,%M",
+[0xDF]	RM,0,		"PANDN %m,%M",
+[0xE0]	RM,0,		"PAVGB %m,%M",
+[0xE1]	RM,0,		"PSRAW %m,%M",
+[0xE2]	RM,0,		"PSRAL %m,%M",
+[0xE3]	RM,0,		"PAVGW %m,%M",
+[0xE4]	RM,0,		"PMULHUW %m,%M",
+[0xE5]	RM,0,		"PMULHW %m,%M",
+[0xE7]	RM,0,		"MOVNTQ	%M,%e",
+[0xE8]	RM,0,		"PSUBSB %m,%M",
+[0xE9]	RM,0,		"PSUBSW %m,%M",
+[0xEA]	RM,0,		"PMINSW %m,%M",
+[0xEB]	RM,0,		"POR %m,%M",
+[0xEC]	RM,0,		"PADDSB %m,%M",
+[0xED]	RM,0,		"PADDSW %m,%M",
+[0xEE]	RM,0,		"PMAXSW %m,%M",
+[0xEF]	RM,0,		"PXOR %m,%M",
+[0xF1]	RM,0,		"PSLLW %m,%M",
+[0xF2]	RM,0,		"PSLLL %m,%M",
+[0xF3]	RM,0,		"PSLLQ %m,%M",
+[0xF4]	RM,0,		"PMULULQ	%m,%M",
+[0xF5]	RM,0,		"PMADDWL %m,%M",
+[0xF6]	RM,0,		"PSADBW %m,%M",
+[0xF7]	RMR,0,		"MASKMOVQ	%m,%M",
+[0xF8]	RM,0,		"PSUBB %m,%M",
+[0xF9]	RM,0,		"PSUBW %m,%M",
+[0xFA]	RM,0,		"PSUBL %m,%M",
+[0xFC]	RM,0,		"PADDB %m,%M",
+[0xFD]	RM,0,		"PADDW %m,%M",
+[0xFE]	RM,0,		"PADDL %m,%M",
+
+[0x80]	Iwds,0,		"JOS	%p",
+[0x81]	Iwds,0,		"JOC	%p",
+[0x82]	Iwds,0,		"JCS	%p",
+[0x83]	Iwds,0,		"JCC	%p",
+[0x84]	Iwds,0,		"JEQ	%p",
+[0x85]	Iwds,0,		"JNE	%p",
+[0x86]	Iwds,0,		"JLS	%p",
+[0x87]	Iwds,0,		"JHI	%p",
+[0x88]	Iwds,0,		"JMI	%p",
+[0x89]	Iwds,0,		"JPL	%p",
+[0x8a]	Iwds,0,		"JPS	%p",
+[0x8b]	Iwds,0,		"JPC	%p",
+[0x8c]	Iwds,0,		"JLT	%p",
+[0x8d]	Iwds,0,		"JGE	%p",
+[0x8e]	Iwds,0,		"JLE	%p",
+[0x8f]	Iwds,0,		"JGT	%p",
+[0x90]	RMB,0,		"SETOS	%e",
+[0x91]	RMB,0,		"SETOC	%e",
+[0x92]	RMB,0,		"SETCS	%e",
+[0x93]	RMB,0,		"SETCC	%e",
+[0x94]	RMB,0,		"SETEQ	%e",
+[0x95]	RMB,0,		"SETNE	%e",
+[0x96]	RMB,0,		"SETLS	%e",
+[0x97]	RMB,0,		"SETHI	%e",
+[0x98]	RMB,0,		"SETMI	%e",
+[0x99]	RMB,0,		"SETPL	%e",
+[0x9a]	RMB,0,		"SETPS	%e",
+[0x9b]	RMB,0,		"SETPC	%e",
+[0x9c]	RMB,0,		"SETLT	%e",
+[0x9d]	RMB,0,		"SETGE	%e",
+[0x9e]	RMB,0,		"SETLE	%e",
+[0x9f]	RMB,0,		"SETGT	%e",
+[0xa0]	0,0,		"PUSHL	FS",
+[0xa1]	0,0,		"POPL	FS",
+[0xa2]	0,0,		"CPUID",
+[0xa3]	RM,0,		"BT%S	%r,%e",
+[0xa4]	RM,Ib,		"SHLD%S	%r,%i,%e",
+[0xa5]	RM,0,		"SHLD%S	%r,CL,%e",
+[0xa8]	0,0,		"PUSHL	GS",
+[0xa9]	0,0,		"POPL	GS",
+[0xab]	RM,0,		"BTS%S	%r,%e",
+[0xac]	RM,Ib,		"SHRD%S	%r,%i,%e",
+[0xad]	RM,0,		"SHRD%S	%r,CL,%e",
+[0xaf]	RM,0,		"IMUL%S	%e,%r",
+[0xb2]	RMM,0,		"LSS	%e,%r",
+[0xb3]	RM,0,		"BTR%S	%r,%e",
+[0xb4]	RMM,0,		"LFS	%e,%r",
+[0xb5]	RMM,0,		"LGS	%e,%r",
+[0xb6]	RMB,0,		"MOVBZX	%e,%R",
+[0xb7]	RM,0,		"MOVWZX	%e,%R",
+[0xba]	RMOP,0,		optab0FBA,
+[0xbb]	RM,0,		"BTC%S	%e,%r",
+[0xbc]	RM,0,		"BSF%S	%e,%r",
+[0xbd]	RM,0,		"BSR%S	%e,%r",
+[0xbe]	RMB,0,		"MOVBSX	%e,%R",
+[0xbf]	RM,0,		"MOVWSX	%e,%R",
+[0xc7]	RMOP,0,		optab0FC7,
+};
+
+static Optable optab80[8]=
+{
+[0x00]	Ib,0,		"ADDB	%i,%e",
+[0x01]	Ib,0,		"ORB	%i,%e",
+[0x02]	Ib,0,		"ADCB	%i,%e",
+[0x03]	Ib,0,		"SBBB	%i,%e",
+[0x04]	Ib,0,		"ANDB	%i,%e",
+[0x05]	Ib,0,		"SUBB	%i,%e",
+[0x06]	Ib,0,		"XORB	%i,%e",
+[0x07]	Ib,0,		"CMPB	%e,%i",
+};
+
+static Optable optab81[8]=
+{
+[0x00]	Iwd,0,		"ADD%S	%i,%e",
+[0x01]	Iwd,0,		"OR%S	%i,%e",
+[0x02]	Iwd,0,		"ADC%S	%i,%e",
+[0x03]	Iwd,0,		"SBB%S	%i,%e",
+[0x04]	Iwd,0,		"AND%S	%i,%e",
+[0x05]	Iwd,0,		"SUB%S	%i,%e",
+[0x06]	Iwd,0,		"XOR%S	%i,%e",
+[0x07]	Iwd,0,		"CMP%S	%e,%i",
+};
+
+static Optable optab83[8]=
+{
+[0x00]	Ibs,0,		"ADD%S	%i,%e",
+[0x01]	Ibs,0,		"OR%S	%i,%e",
+[0x02]	Ibs,0,		"ADC%S	%i,%e",
+[0x03]	Ibs,0,		"SBB%S	%i,%e",
+[0x04]	Ibs,0,		"AND%S	%i,%e",
+[0x05]	Ibs,0,		"SUB%S	%i,%e",
+[0x06]	Ibs,0,		"XOR%S	%i,%e",
+[0x07]	Ibs,0,		"CMP%S	%e,%i",
+};
+
+static Optable optabC0[8] =
+{
+[0x00]	Ib,0,		"ROLB	%i,%e",
+[0x01]	Ib,0,		"RORB	%i,%e",
+[0x02]	Ib,0,		"RCLB	%i,%e",
+[0x03]	Ib,0,		"RCRB	%i,%e",
+[0x04]	Ib,0,		"SHLB	%i,%e",
+[0x05]	Ib,0,		"SHRB	%i,%e",
+[0x07]	Ib,0,		"SARB	%i,%e",
+};
+
+static Optable optabC1[8] =
+{
+[0x00]	Ib,0,		"ROL%S	%i,%e",
+[0x01]	Ib,0,		"ROR%S	%i,%e",
+[0x02]	Ib,0,		"RCL%S	%i,%e",
+[0x03]	Ib,0,		"RCR%S	%i,%e",
+[0x04]	Ib,0,		"SHL%S	%i,%e",
+[0x05]	Ib,0,		"SHR%S	%i,%e",
+[0x07]	Ib,0,		"SAR%S	%i,%e",
+};
+
+static Optable optabD0[8] =
+{
+[0x00]	0,0,		"ROLB	%e",
+[0x01]	0,0,		"RORB	%e",
+[0x02]	0,0,		"RCLB	%e",
+[0x03]	0,0,		"RCRB	%e",
+[0x04]	0,0,		"SHLB	%e",
+[0x05]	0,0,		"SHRB	%e",
+[0x07]	0,0,		"SARB	%e",
+};
+
+static Optable optabD1[8] =
+{
+[0x00]	0,0,		"ROL%S	%e",
+[0x01]	0,0,		"ROR%S	%e",
+[0x02]	0,0,		"RCL%S	%e",
+[0x03]	0,0,		"RCR%S	%e",
+[0x04]	0,0,		"SHL%S	%e",
+[0x05]	0,0,		"SHR%S	%e",
+[0x07]	0,0,		"SAR%S	%e",
+};
+
+static Optable optabD2[8] =
+{
+[0x00]	0,0,		"ROLB	CL,%e",
+[0x01]	0,0,		"RORB	CL,%e",
+[0x02]	0,0,		"RCLB	CL,%e",
+[0x03]	0,0,		"RCRB	CL,%e",
+[0x04]	0,0,		"SHLB	CL,%e",
+[0x05]	0,0,		"SHRB	CL,%e",
+[0x07]	0,0,		"SARB	CL,%e",
+};
+
+static Optable optabD3[8] =
+{
+[0x00]	0,0,		"ROL%S	CL,%e",
+[0x01]	0,0,		"ROR%S	CL,%e",
+[0x02]	0,0,		"RCL%S	CL,%e",
+[0x03]	0,0,		"RCR%S	CL,%e",
+[0x04]	0,0,		"SHL%S	CL,%e",
+[0x05]	0,0,		"SHR%S	CL,%e",
+[0x07]	0,0,		"SAR%S	CL,%e",
+};
+
+static Optable optabD8[8+8] =
+{
+[0x00]	0,0,		"FADDF	%e,F0",
+[0x01]	0,0,		"FMULF	%e,F0",
+[0x02]	0,0,		"FCOMF	%e,F0",
+[0x03]	0,0,		"FCOMFP	%e,F0",
+[0x04]	0,0,		"FSUBF	%e,F0",
+[0x05]	0,0,		"FSUBRF	%e,F0",
+[0x06]	0,0,		"FDIVF	%e,F0",
+[0x07]	0,0,		"FDIVRF	%e,F0",
+[0x08]	0,0,		"FADDD	%f,F0",
+[0x09]	0,0,		"FMULD	%f,F0",
+[0x0a]	0,0,		"FCOMD	%f,F0",
+[0x0b]	0,0,		"FCOMPD	%f,F0",
+[0x0c]	0,0,		"FSUBD	%f,F0",
+[0x0d]	0,0,		"FSUBRD	%f,F0",
+[0x0e]	0,0,		"FDIVD	%f,F0",
+[0x0f]	0,0,		"FDIVRD	%f,F0",
+};
+/*
+ *	optabD9 and optabDB use the following encoding:
+ *	if (0 <= modrm <= 2) instruction = optabDx[modrm&0x07];
+ *	else instruction = optabDx[(modrm&0x3f)+8];
+ *
+ *	the instructions for MOD == 3, follow the 8 instructions
+ *	for the other MOD values stored at the front of the table.
+ */
+static Optable optabD9[64+8] =
+{
+[0x00]	0,0,		"FMOVF	%e,F0",
+[0x02]	0,0,		"FMOVF	F0,%e",
+[0x03]	0,0,		"FMOVFP	F0,%e",
+[0x04]	0,0,		"FLDENV%S %e",
+[0x05]	0,0,		"FLDCW	%e",
+[0x06]	0,0,		"FSTENV%S %e",
+[0x07]	0,0,		"FSTCW	%e",
+[0x08]	0,0,		"FMOVD	F0,F0",		/* Mod R/M = 11xx xxxx*/
+[0x09]	0,0,		"FMOVD	F1,F0",
+[0x0a]	0,0,		"FMOVD	F2,F0",
+[0x0b]	0,0,		"FMOVD	F3,F0",
+[0x0c]	0,0,		"FMOVD	F4,F0",
+[0x0d]	0,0,		"FMOVD	F5,F0",
+[0x0e]	0,0,		"FMOVD	F6,F0",
+[0x0f]	0,0,		"FMOVD	F7,F0",
+[0x10]	0,0,		"FXCHD	F0,F0",
+[0x11]	0,0,		"FXCHD	F1,F0",
+[0x12]	0,0,		"FXCHD	F2,F0",
+[0x13]	0,0,		"FXCHD	F3,F0",
+[0x14]	0,0,		"FXCHD	F4,F0",
+[0x15]	0,0,		"FXCHD	F5,F0",
+[0x16]	0,0,		"FXCHD	F6,F0",
+[0x17]	0,0,		"FXCHD	F7,F0",
+[0x18]	0,0,		"FNOP",
+[0x28]	0,0,		"FCHS",
+[0x29]	0,0,		"FABS",
+[0x2c]	0,0,		"FTST",
+[0x2d]	0,0,		"FXAM",
+[0x30]	0,0,		"FLD1",
+[0x31]	0,0,		"FLDL2T",
+[0x32]	0,0,		"FLDL2E",
+[0x33]	0,0,		"FLDPI",
+[0x34]	0,0,		"FLDLG2",
+[0x35]	0,0,		"FLDLN2",
+[0x36]	0,0,		"FLDZ",
+[0x38]	0,0,		"F2XM1",
+[0x39]	0,0,		"FYL2X",
+[0x3a]	0,0,		"FPTAN",
+[0x3b]	0,0,		"FPATAN",
+[0x3c]	0,0,		"FXTRACT",
+[0x3d]	0,0,		"FPREM1",
+[0x3e]	0,0,		"FDECSTP",
+[0x3f]	0,0,		"FNCSTP",
+[0x40]	0,0,		"FPREM",
+[0x41]	0,0,		"FYL2XP1",
+[0x42]	0,0,		"FSQRT",
+[0x43]	0,0,		"FSINCOS",
+[0x44]	0,0,		"FRNDINT",
+[0x45]	0,0,		"FSCALE",
+[0x46]	0,0,		"FSIN",
+[0x47]	0,0,		"FCOS",
+};
+
+static Optable optabDA[8+8] =
+{
+[0x00]	0,0,		"FADDL	%e,F0",
+[0x01]	0,0,		"FMULL	%e,F0",
+[0x02]	0,0,		"FCOML	%e,F0",
+[0x03]	0,0,		"FCOMLP	%e,F0",
+[0x04]	0,0,		"FSUBL	%e,F0",
+[0x05]	0,0,		"FSUBRL	%e,F0",
+[0x06]	0,0,		"FDIVL	%e,F0",
+[0x07]	0,0,		"FDIVRL	%e,F0",
+[0x0d]	R1,0,		"FUCOMPP",
+};
+
+static Optable optabDB[8+64] =
+{
+[0x00]	0,0,		"FMOVL	%e,F0",
+[0x02]	0,0,		"FMOVL	F0,%e",
+[0x03]	0,0,		"FMOVLP	F0,%e",
+[0x05]	0,0,		"FMOVX	%e,F0",
+[0x07]	0,0,		"FMOVXP	F0,%e",
+[0x2a]	0,0,		"FCLEX",
+[0x2b]	0,0,		"FINIT",
+};
+
+static Optable optabDC[8+8] =
+{
+[0x00]	0,0,		"FADDD	%e,F0",
+[0x01]	0,0,		"FMULD	%e,F0",
+[0x02]	0,0,		"FCOMD	%e,F0",
+[0x03]	0,0,		"FCOMDP	%e,F0",
+[0x04]	0,0,		"FSUBD	%e,F0",
+[0x05]	0,0,		"FSUBRD	%e,F0",
+[0x06]	0,0,		"FDIVD	%e,F0",
+[0x07]	0,0,		"FDIVRD	%e,F0",
+[0x08]	0,0,		"FADDD	F0,%f",
+[0x09]	0,0,		"FMULD	F0,%f",
+[0x0c]	0,0,		"FSUBRD	F0,%f",
+[0x0d]	0,0,		"FSUBD	F0,%f",
+[0x0e]	0,0,		"FDIVRD	F0,%f",
+[0x0f]	0,0,		"FDIVD	F0,%f",
+};
+
+static Optable optabDD[8+8] =
+{
+[0x00]	0,0,		"FMOVD	%e,F0",
+[0x02]	0,0,		"FMOVD	F0,%e",
+[0x03]	0,0,		"FMOVDP	F0,%e",
+[0x04]	0,0,		"FRSTOR%S %e",
+[0x06]	0,0,		"FSAVE%S %e",
+[0x07]	0,0,		"FSTSW	%e",
+[0x08]	0,0,		"FFREED	%f",
+[0x0a]	0,0,		"FMOVD	%f,F0",
+[0x0b]	0,0,		"FMOVDP	%f,F0",
+[0x0c]	0,0,		"FUCOMD	%f,F0",
+[0x0d]	0,0,		"FUCOMDP %f,F0",
+};
+
+static Optable optabDE[8+8] =
+{
+[0x00]	0,0,		"FADDW	%e,F0",
+[0x01]	0,0,		"FMULW	%e,F0",
+[0x02]	0,0,		"FCOMW	%e,F0",
+[0x03]	0,0,		"FCOMWP	%e,F0",
+[0x04]	0,0,		"FSUBW	%e,F0",
+[0x05]	0,0,		"FSUBRW	%e,F0",
+[0x06]	0,0,		"FDIVW	%e,F0",
+[0x07]	0,0,		"FDIVRW	%e,F0",
+[0x08]	0,0,		"FADDDP	F0,%f",
+[0x09]	0,0,		"FMULDP	F0,%f",
+[0x0b]	R1,0,		"FCOMPDP",
+[0x0c]	0,0,		"FSUBRDP F0,%f",
+[0x0d]	0,0,		"FSUBDP	F0,%f",
+[0x0e]	0,0,		"FDIVRDP F0,%f",
+[0x0f]	0,0,		"FDIVDP	F0,%f",
+};
+
+static Optable optabDF[8+8] =
+{
+[0x00]	0,0,		"FMOVW	%e,F0",
+[0x02]	0,0,		"FMOVW	F0,%e",
+[0x03]	0,0,		"FMOVWP	F0,%e",
+[0x04]	0,0,		"FBLD	%e",
+[0x05]	0,0,		"FMOVL	%e,F0",
+[0x06]	0,0,		"FBSTP	%e",
+[0x07]	0,0,		"FMOVLP	F0,%e",
+[0x0c]	R0,0,		"FSTSW	%OAX",
+};
+
+static Optable optabF6[8] =
+{
+[0x00]	Ib,0,		"TESTB	%i,%e",
+[0x02]	0,0,		"NOTB	%e",
+[0x03]	0,0,		"NEGB	%e",
+[0x04]	0,0,		"MULB	AL,%e",
+[0x05]	0,0,		"IMULB	AL,%e",
+[0x06]	0,0,		"DIVB	AL,%e",
+[0x07]	0,0,		"IDIVB	AL,%e",
+};
+
+static Optable optabF7[8] =
+{
+[0x00]	Iwd,0,		"TEST%S	%i,%e",
+[0x02]	0,0,		"NOT%S	%e",
+[0x03]	0,0,		"NEG%S	%e",
+[0x04]	0,0,		"MUL%S	%OAX,%e",
+[0x05]	0,0,		"IMUL%S	%OAX,%e",
+[0x06]	0,0,		"DIV%S	%OAX,%e",
+[0x07]	0,0,		"IDIV%S	%OAX,%e",
+};
+
+static Optable optabFE[8] =
+{
+[0x00]	0,0,		"INCB	%e",
+[0x01]	0,0,		"DECB	%e",
+};
+
+static Optable optabFF[8] =
+{
+[0x00]	0,0,		"INC%S	%e",
+[0x01]	0,0,		"DEC%S	%e",
+[0x02]	JUMP,0,		"CALL*	%e",
+[0x03]	JUMP,0,		"CALLF*	%e",
+[0x04]	JUMP,0,		"JMP*	%e",
+[0x05]	JUMP,0,		"JMPF*	%e",
+[0x06]	0,0,		"PUSHL	%e",
+};
+
+static Optable optable[256+1] =
+{
+[0x00]	RMB,0,		"ADDB	%r,%e",
+[0x01]	RM,0,		"ADD%S	%r,%e",
+[0x02]	RMB,0,		"ADDB	%e,%r",
+[0x03]	RM,0,		"ADD%S	%e,%r",
+[0x04]	Ib,0,		"ADDB	%i,AL",
+[0x05]	Iwd,0,		"ADD%S	%i,%OAX",
+[0x06]	0,0,		"PUSHL	ES",
+[0x07]	0,0,		"POPL	ES",
+[0x08]	RMB,0,		"ORB	%r,%e",
+[0x09]	RM,0,		"OR%S	%r,%e",
+[0x0a]	RMB,0,		"ORB	%e,%r",
+[0x0b]	RM,0,		"OR%S	%e,%r",
+[0x0c]	Ib,0,		"ORB	%i,AL",
+[0x0d]	Iwd,0,		"OR%S	%i,%OAX",
+[0x0e]	0,0,		"PUSHL	CS",
+[0x0f]	AUXMM,0,	optab0F,
+[0x10]	RMB,0,		"ADCB	%r,%e",
+[0x11]	RM,0,		"ADC%S	%r,%e",
+[0x12]	RMB,0,		"ADCB	%e,%r",
+[0x13]	RM,0,		"ADC%S	%e,%r",
+[0x14]	Ib,0,		"ADCB	%i,AL",
+[0x15]	Iwd,0,		"ADC%S	%i,%OAX",
+[0x16]	0,0,		"PUSHL	SS",
+[0x17]	0,0,		"POPL	SS",
+[0x18]	RMB,0,		"SBBB	%r,%e",
+[0x19]	RM,0,		"SBB%S	%r,%e",
+[0x1a]	RMB,0,		"SBBB	%e,%r",
+[0x1b]	RM,0,		"SBB%S	%e,%r",
+[0x1c]	Ib,0,		"SBBB	%i,AL",
+[0x1d]	Iwd,0,		"SBB%S	%i,%OAX",
+[0x1e]	0,0,		"PUSHL	DS",
+[0x1f]	0,0,		"POPL	DS",
+[0x20]	RMB,0,		"ANDB	%r,%e",
+[0x21]	RM,0,		"AND%S	%r,%e",
+[0x22]	RMB,0,		"ANDB	%e,%r",
+[0x23]	RM,0,		"AND%S	%e,%r",
+[0x24]	Ib,0,		"ANDB	%i,AL",
+[0x25]	Iwd,0,		"AND%S	%i,%OAX",
+[0x26]	SEG,0,		"ES:",
+[0x27]	0,0,		"DAA",
+[0x28]	RMB,0,		"SUBB	%r,%e",
+[0x29]	RM,0,		"SUB%S	%r,%e",
+[0x2a]	RMB,0,		"SUBB	%e,%r",
+[0x2b]	RM,0,		"SUB%S	%e,%r",
+[0x2c]	Ib,0,		"SUBB	%i,AL",
+[0x2d]	Iwd,0,		"SUB%S	%i,%OAX",
+[0x2e]	SEG,0,		"CS:",
+[0x2f]	0,0,		"DAS",
+[0x30]	RMB,0,		"XORB	%r,%e",
+[0x31]	RM,0,		"XOR%S	%r,%e",
+[0x32]	RMB,0,		"XORB	%e,%r",
+[0x33]	RM,0,		"XOR%S	%e,%r",
+[0x34]	Ib,0,		"XORB	%i,AL",
+[0x35]	Iwd,0,		"XOR%S	%i,%OAX",
+[0x36]	SEG,0,		"SS:",
+[0x37]	0,0,		"AAA",
+[0x38]	RMB,0,		"CMPB	%r,%e",
+[0x39]	RM,0,		"CMP%S	%r,%e",
+[0x3a]	RMB,0,		"CMPB	%e,%r",
+[0x3b]	RM,0,		"CMP%S	%e,%r",
+[0x3c]	Ib,0,		"CMPB	%i,AL",
+[0x3d]	Iwd,0,		"CMP%S	%i,%OAX",
+[0x3e]	SEG,0,		"DS:",
+[0x3f]	0,0,		"AAS",
+[0x40]	0,0,		"INC%S	%OAX",
+[0x41]	0,0,		"INC%S	%OCX",
+[0x42]	0,0,		"INC%S	%ODX",
+[0x43]	0,0,		"INC%S	%OBX",
+[0x44]	0,0,		"INC%S	%OSP",
+[0x45]	0,0,		"INC%S	%OBP",
+[0x46]	0,0,		"INC%S	%OSI",
+[0x47]	0,0,		"INC%S	%ODI",
+[0x48]	0,0,		"DEC%S	%OAX",
+[0x49]	0,0,		"DEC%S	%OCX",
+[0x4a]	0,0,		"DEC%S	%ODX",
+[0x4b]	0,0,		"DEC%S	%OBX",
+[0x4c]	0,0,		"DEC%S	%OSP",
+[0x4d]	0,0,		"DEC%S	%OBP",
+[0x4e]	0,0,		"DEC%S	%OSI",
+[0x4f]	0,0,		"DEC%S	%ODI",
+[0x50]	0,0,		"PUSH%S	%OAX",
+[0x51]	0,0,		"PUSH%S	%OCX",
+[0x52]	0,0,		"PUSH%S	%ODX",
+[0x53]	0,0,		"PUSH%S	%OBX",
+[0x54]	0,0,		"PUSH%S	%OSP",
+[0x55]	0,0,		"PUSH%S	%OBP",
+[0x56]	0,0,		"PUSH%S	%OSI",
+[0x57]	0,0,		"PUSH%S	%ODI",
+[0x58]	0,0,		"POP%S	%OAX",
+[0x59]	0,0,		"POP%S	%OCX",
+[0x5a]	0,0,		"POP%S	%ODX",
+[0x5b]	0,0,		"POP%S	%OBX",
+[0x5c]	0,0,		"POP%S	%OSP",
+[0x5d]	0,0,		"POP%S	%OBP",
+[0x5e]	0,0,		"POP%S	%OSI",
+[0x5f]	0,0,		"POP%S	%ODI",
+[0x60]	0,0,		"PUSHA%S",
+[0x61]	0,0,		"POPA%S",
+[0x62]	RMM,0,		"BOUND	%e,%r",
+[0x63]	RM,0,		"ARPL	%r,%e",
+[0x64]	SEG,0,		"FS:",
+[0x65]	SEG,0,		"GS:",
+[0x66]	OPOVER,0,	"",
+[0x67]	ADDOVER,0,	"",
+[0x68]	Iwd,0,		"PUSH%S	%i",
+[0x69]	RM,Iwd,		"IMUL%S	%e,%i,%r",
+[0x6a]	Ib,0,		"PUSH%S	%i",
+[0x6b]	RM,Ibs,		"IMUL%S	%e,%i,%r",
+[0x6c]	0,0,		"INSB	DX,(%ODI)",
+[0x6d]	0,0,		"INS%S	DX,(%ODI)",
+[0x6e]	0,0,		"OUTSB	(%ASI),DX",
+[0x6f]	0,0,		"OUTS%S	(%ASI),DX",
+[0x70]	Jbs,0,		"JOS	%p",
+[0x71]	Jbs,0,		"JOC	%p",
+[0x72]	Jbs,0,		"JCS	%p",
+[0x73]	Jbs,0,		"JCC	%p",
+[0x74]	Jbs,0,		"JEQ	%p",
+[0x75]	Jbs,0,		"JNE	%p",
+[0x76]	Jbs,0,		"JLS	%p",
+[0x77]	Jbs,0,		"JHI	%p",
+[0x78]	Jbs,0,		"JMI	%p",
+[0x79]	Jbs,0,		"JPL	%p",
+[0x7a]	Jbs,0,		"JPS	%p",
+[0x7b]	Jbs,0,		"JPC	%p",
+[0x7c]	Jbs,0,		"JLT	%p",
+[0x7d]	Jbs,0,		"JGE	%p",
+[0x7e]	Jbs,0,		"JLE	%p",
+[0x7f]	Jbs,0,		"JGT	%p",
+[0x80]	RMOPB,0,	optab80,
+[0x81]	RMOP,0,		optab81,
+[0x83]	RMOP,0,		optab83,
+[0x84]	RMB,0,		"TESTB	%r,%e",
+[0x85]	RM,0,		"TEST%S	%r,%e",
+[0x86]	RMB,0,		"XCHGB	%r,%e",
+[0x87]	RM,0,		"XCHG%S	%r,%e",
+[0x88]	RMB,0,		"MOVB	%r,%e",
+[0x89]	RM,0,		"MOV%S	%r,%e",
+[0x8a]	RMB,0,		"MOVB	%e,%r",
+[0x8b]	RM,0,		"MOV%S	%e,%r",
+[0x8c]	RM,0,		"MOVW	%g,%e",
+[0x8d]	RM,0,		"LEA%S	%e,%r",
+[0x8e]	RM,0,		"MOVW	%e,%g",
+[0x8f]	RM,0,		"POP%S	%e",
+[0x90]	0,0,		"NOP",
+[0x91]	0,0,		"XCHG	%OCX,%OAX",
+[0x92]	0,0,		"XCHG	%ODX,%OAX",
+[0x93]	0,0,		"XCHG	%OBX,%OAX",
+[0x94]	0,0,		"XCHG	%OSP,%OAX",
+[0x95]	0,0,		"XCHG	%OBP,%OAX",
+[0x96]	0,0,		"XCHG	%OSI,%OAX",
+[0x97]	0,0,		"XCHG	%ODI,%OAX",
+[0x98]	0,0,		"%W",			/* miserable CBW or CWDE */
+[0x99]	0,0,		"%w",			/* idiotic CWD or CDQ */
+[0x9a]	PTR,0,		"CALL%S	%d",
+[0x9b]	0,0,		"WAIT",
+[0x9c]	0,0,		"PUSHF",
+[0x9d]	0,0,		"POPF",
+[0x9e]	0,0,		"SAHF",
+[0x9f]	0,0,		"LAHF",
+[0xa0]	Awd,0,		"MOVB	%i,AL",
+[0xa1]	Awd,0,		"MOV%S	%i,%OAX",
+[0xa2]	Awd,0,		"MOVB	AL,%i",
+[0xa3]	Awd,0,		"MOV%S	%OAX,%i",
+[0xa4]	0,0,		"MOVSB	(%ASI),(%ADI)",
+[0xa5]	0,0,		"MOVS%S	(%ASI),(%ADI)",
+[0xa6]	0,0,		"CMPSB	(%ASI),(%ADI)",
+[0xa7]	0,0,		"CMPS%S	(%ASI),(%ADI)",
+[0xa8]	Ib,0,		"TESTB	%i,AL",
+[0xa9]	Iwd,0,		"TEST%S	%i,%OAX",
+[0xaa]	0,0,		"STOSB	AL,(%ADI)",
+[0xab]	0,0,		"STOS%S	%OAX,(%ADI)",
+[0xac]	0,0,		"LODSB	(%ASI),AL",
+[0xad]	0,0,		"LODS%S	(%ASI),%OAX",
+[0xae]	0,0,		"SCASB	(%ADI),AL",
+[0xaf]	0,0,		"SCAS%S	(%ADI),%OAX",
+[0xb0]	Ib,0,		"MOVB	%i,AL",
+[0xb1]	Ib,0,		"MOVB	%i,CL",
+[0xb2]	Ib,0,		"MOVB	%i,DL",
+[0xb3]	Ib,0,		"MOVB	%i,BL",
+[0xb4]	Ib,0,		"MOVB	%i,AH",
+[0xb5]	Ib,0,		"MOVB	%i,CH",
+[0xb6]	Ib,0,		"MOVB	%i,DH",
+[0xb7]	Ib,0,		"MOVB	%i,BH",
+[0xb8]	Iwdq,0,		"MOV%S	%i,%OAX",
+[0xb9]	Iwdq,0,		"MOV%S	%i,%OCX",
+[0xba]	Iwdq,0,		"MOV%S	%i,%ODX",
+[0xbb]	Iwdq,0,		"MOV%S	%i,%OBX",
+[0xbc]	Iwdq,0,		"MOV%S	%i,%OSP",
+[0xbd]	Iwdq,0,		"MOV%S	%i,%OBP",
+[0xbe]	Iwdq,0,		"MOV%S	%i,%OSI",
+[0xbf]	Iwdq,0,		"MOV%S	%i,%ODI",
+[0xc0]	RMOPB,0,	optabC0,
+[0xc1]	RMOP,0,		optabC1,
+[0xc2]	Iw,0,		"RET	%i",
+[0xc3]	RET,0,		"RET",
+[0xc4]	RM,0,		"LES	%e,%r",
+[0xc5]	RM,0,		"LDS	%e,%r",
+[0xc6]	RMB,Ib,		"MOVB	%i,%e",
+[0xc7]	RM,Iwd,		"MOV%S	%i,%e",
+[0xc8]	Iw2,Ib,		"ENTER	%i,%I",		/* loony ENTER */
+[0xc9]	RET,0,		"LEAVE",		/* bizarre LEAVE */
+[0xca]	Iw,0,		"RETF	%i",
+[0xcb]	RET,0,		"RETF",
+[0xcc]	0,0,		"INT	3",
+[0xcd]	Ib,0,		"INTB	%i",
+[0xce]	0,0,		"INTO",
+[0xcf]	0,0,		"IRET",
+[0xd0]	RMOPB,0,	optabD0,
+[0xd1]	RMOP,0,		optabD1,
+[0xd2]	RMOPB,0,	optabD2,
+[0xd3]	RMOP,0,		optabD3,
+[0xd4]	OA,0,		"AAM",
+[0xd5]	OA,0,		"AAD",
+[0xd7]	0,0,		"XLAT",
+[0xd8]	FRMOP,0,	optabD8,
+[0xd9]	FRMEX,0,	optabD9,
+[0xda]	FRMOP,0,	optabDA,
+[0xdb]	FRMEX,0,	optabDB,
+[0xdc]	FRMOP,0,	optabDC,
+[0xdd]	FRMOP,0,	optabDD,
+[0xde]	FRMOP,0,	optabDE,
+[0xdf]	FRMOP,0,	optabDF,
+[0xe0]	Jbs,0,		"LOOPNE	%p",
+[0xe1]	Jbs,0,		"LOOPE	%p",
+[0xe2]	Jbs,0,		"LOOP	%p",
+[0xe3]	Jbs,0,		"JCXZ	%p",
+[0xe4]	Ib,0,		"INB	%i,AL",
+[0xe5]	Ib,0,		"IN%S	%i,%OAX",
+[0xe6]	Ib,0,		"OUTB	AL,%i",
+[0xe7]	Ib,0,		"OUT%S	%OAX,%i",
+[0xe8]	Iwds,0,		"CALL	%p",
+[0xe9]	Iwds,0,		"JMP	%p",
+[0xea]	PTR,0,		"JMP	%d",
+[0xeb]	Jbs,0,		"JMP	%p",
+[0xec]	0,0,		"INB	DX,AL",
+[0xed]	0,0,		"IN%S	DX,%OAX",
+[0xee]	0,0,		"OUTB	AL,DX",
+[0xef]	0,0,		"OUT%S	%OAX,DX",
+[0xf0]	PRE,0,		"LOCK",
+[0xf2]	OPRE,0,		"REPNE",
+[0xf3]	OPRE,0,		"REP",
+[0xf4]	0,0,		"HLT",
+[0xf5]	0,0,		"CMC",
+[0xf6]	RMOPB,0,	optabF6,
+[0xf7]	RMOP,0,		optabF7,
+[0xf8]	0,0,		"CLC",
+[0xf9]	0,0,		"STC",
+[0xfa]	0,0,		"CLI",
+[0xfb]	0,0,		"STI",
+[0xfc]	0,0,		"CLD",
+[0xfd]	0,0,		"STD",
+[0xfe]	RMOPB,0,	optabFE,
+[0xff]	RMOP,0,		optabFF,
+[0x100]	RM,0,		"MOVLQSX	%r,%e",
+};
+
+/*
+ *  get a byte of the instruction
+ */
+static int
+igetc(Map *map, Instr *ip, uchar *c)
+{
+	if(ip->n+1 > sizeof(ip->mem)){
+		werrstr("instruction too long");
+		return -1;
+	}
+	if (get1(map, ip->addr+ip->n, c, 1) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	ip->mem[ip->n++] = *c;
+	return 1;
+}
+
+/*
+ *  get two bytes of the instruction
+ */
+static int
+igets(Map *map, Instr *ip, ushort *sp)
+{
+	uchar c;
+	ushort s;
+
+	if (igetc(map, ip, &c) < 0)
+		return -1;
+	s = c;
+	if (igetc(map, ip, &c) < 0)
+		return -1;
+	s |= (c<<8);
+	*sp = s;
+	return 1;
+}
+
+/*
+ *  get 4 bytes of the instruction
+ */
+static int
+igetl(Map *map, Instr *ip, uint32 *lp)
+{
+	ushort s;
+	int32	l;
+
+	if (igets(map, ip, &s) < 0)
+		return -1;
+	l = s;
+	if (igets(map, ip, &s) < 0)
+		return -1;
+	l |= (s<<16);
+	*lp = l;
+	return 1;
+}
+
+/*
+ *  get 8 bytes of the instruction
+ *
+static int
+igetq(Map *map, Instr *ip, vlong *qp)
+{
+	uint32	l;
+	uvlong q;
+
+	if (igetl(map, ip, &l) < 0)
+		return -1;
+	q = l;
+	if (igetl(map, ip, &l) < 0)
+		return -1;
+	q |= ((uvlong)l<<32);
+	*qp = q;
+	return 1;
+}
+ */
+
+static int
+getdisp(Map *map, Instr *ip, int mod, int rm, int code, int pcrel)
+{
+	uchar c;
+	ushort s;
+
+	if (mod > 2)
+		return 1;
+	if (mod == 1) {
+		if (igetc(map, ip, &c) < 0)
+			return -1;
+		if (c&0x80)
+			ip->disp = c|0xffffff00;
+		else
+			ip->disp = c&0xff;
+	} else if (mod == 2 || rm == code) {
+		if (ip->asize == 'E') {
+			if (igetl(map, ip, &ip->disp) < 0)
+				return -1;
+			if (mod == 0)
+				ip->rip = pcrel;
+		} else {
+			if (igets(map, ip, &s) < 0)
+				return -1;
+			if (s&0x8000)
+				ip->disp = s|0xffff0000;
+			else
+				ip->disp = s;
+		}
+		if (mod == 0)
+			ip->base = -1;
+	}
+	return 1;
+}
+
+static int
+modrm(Map *map, Instr *ip, uchar c)
+{
+	uchar rm, mod;
+
+	mod = (c>>6)&3;
+	rm = c&7;
+	ip->mod = mod;
+	ip->base = rm;
+	ip->reg = (c>>3)&7;
+	ip->rip = 0;
+	if (mod == 3)			/* register */
+		return 1;
+	if (ip->asize == 0) {		/* 16-bit mode */
+		switch(rm) {
+		case 0:
+			ip->base = BX; ip->index = SI;
+			break;
+		case 1:
+			ip->base = BX; ip->index = DI;
+			break;
+		case 2:
+			ip->base = BP; ip->index = SI;
+			break;
+		case 3:
+			ip->base = BP; ip->index = DI;
+			break;
+		case 4:
+			ip->base = SI;
+			break;
+		case 5:
+			ip->base = DI;
+			break;
+		case 6:
+			ip->base = BP;
+			break;
+		case 7:
+			ip->base = BX;
+			break;
+		default:
+			break;
+		}
+		return getdisp(map, ip, mod, rm, 6, 0);
+	}
+	if (rm == 4) {	/* scummy sib byte */
+		if (igetc(map, ip, &c) < 0)
+			return -1;
+		ip->ss = (c>>6)&0x03;
+		ip->index = (c>>3)&0x07;
+		if (ip->index == 4)
+			ip->index = -1;
+		ip->base = c&0x07;
+		return getdisp(map, ip, mod, ip->base, 5, 0);
+	}
+	return getdisp(map, ip, mod, rm, 5, ip->amd64);
+}
+
+static Optable *
+mkinstr(Map *map, Instr *ip, uvlong pc)
+{
+	int i, n, norex;
+	uchar c;
+	ushort s;
+	Optable *op, *obase;
+	char buf[128];
+
+	memset(ip, 0, sizeof(*ip));
+	norex = 1;
+	ip->base = -1;
+	ip->index = -1;
+	if(asstype == AI8086)
+		ip->osize = 'W';
+	else {
+		ip->osize = 'L';
+		ip->asize = 'E';
+		ip->amd64 = asstype != AI386;
+		norex = 0;
+	}
+	ip->addr = pc;
+	if (igetc(map, ip, &c) < 0)
+		return 0;
+	obase = optable;
+newop:
+	if(ip->amd64 && !norex){
+		if(c >= 0x40 && c <= 0x4f) {
+			ip->rex = c;
+			if(igetc(map, ip, &c) < 0)
+				return 0;
+		}
+		if(c == 0x63){
+			op = &obase[0x100];	/* MOVLQSX */
+			goto hack;
+		}
+	}
+	op = &obase[c];
+hack:
+	if (op->proto == 0) {
+badop:
+		n = snprint(buf, sizeof(buf), "opcode: ??");
+		for (i = 0; i < ip->n && n < sizeof(buf)-3; i++, n+=2)
+			_hexify(buf+n, ip->mem[i], 1);
+		strcpy(buf+n, "??");
+		werrstr(buf);
+		return 0;
+	}
+	for(i = 0; i < 2 && op->operand[i]; i++) {
+		switch(op->operand[i]) {
+		case Ib:	/* 8-bit immediate - (no sign extension)*/
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			ip->imm = c&0xff;
+			ip->imm64 = ip->imm;
+			break;
+		case Jbs:	/* 8-bit jump immediate (sign extended) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c&0x80)
+				ip->imm = c|0xffffff00;
+			else
+				ip->imm = c&0xff;
+			ip->imm64 = (int32)ip->imm;
+			ip->jumptype = Jbs;
+			break;
+		case Ibs:	/* 8-bit immediate (sign extended) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c&0x80)
+				if (ip->osize == 'L')
+					ip->imm = c|0xffffff00;
+				else
+					ip->imm = c|0xff00;
+			else
+				ip->imm = c&0xff;
+			ip->imm64 = (int32)ip->imm;
+			break;
+		case Iw:	/* 16-bit immediate -> imm */
+			if (igets(map, ip, &s) < 0)
+				return 0;
+			ip->imm = s&0xffff;
+			ip->imm64 = ip->imm;
+			ip->jumptype = Iw;
+			break;
+		case Iw2:	/* 16-bit immediate -> in imm2*/
+			if (igets(map, ip, &s) < 0)
+				return 0;
+			ip->imm2 = s&0xffff;
+			break;
+		case Iwd:	/* Operand-sized immediate (no sign extension unless 64 bits)*/
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+				ip->imm64 = ip->imm;
+				if(ip->rex&REXW && (ip->imm & (1<<31)) != 0)
+					ip->imm64 |= (vlong)~0 << 32;
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->imm = s&0xffff;
+				ip->imm64 = ip->imm;
+			}
+			break;
+		case Iwdq:	/* Operand-sized immediate, possibly big */
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+				ip->imm64 = ip->imm;
+				if (ip->rex & REXW) {
+					uint32 l;
+					if (igetl(map, ip, &l) < 0)
+						return 0;
+					ip->imm64 |= (uvlong)l << 32;
+				}
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->imm = s&0xffff;
+			}
+			break;
+		case Awd:	/* Address-sized immediate (no sign extension)*/
+			if (ip->asize == 'E') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+				/* TO DO: REX */
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->imm = s&0xffff;
+			}
+			break;
+		case Iwds:	/* Operand-sized immediate (sign extended) */
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				if (s&0x8000)
+					ip->imm = s|0xffff0000;
+				else
+					ip->imm = s&0xffff;
+			}
+			ip->jumptype = Iwds;
+			break;
+		case OA:	/* literal 0x0a byte */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c != 0x0a)
+				goto badop;
+			break;
+		case R0:	/* base register must be R0 */
+			if (ip->base != 0)
+				goto badop;
+			break;
+		case R1:	/* base register must be R1 */
+			if (ip->base != 1)
+				goto badop;
+			break;
+		case RMB:	/* R/M field with byte register (/r)*/
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			ip->osize = 'B';
+			break;
+		case RM:	/* R/M field with register (/r) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			break;
+		case RMOPB:	/* R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			c = ip->reg;		/* secondary op code */
+			obase = (Optable*)op->proto;
+			ip->osize = 'B';
+			goto newop;
+		case RMOP:	/* R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			obase = (Optable*)op->proto;
+			if(ip->amd64 && obase == optab0F01 && c == 0xF8)
+				return optab0F01F8;
+			c = ip->reg;
+			goto newop;
+		case FRMOP:	/* FP R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			if ((c&0xc0) == 0xc0)
+				c = ip->reg+8;		/* 16 entry table */
+			else
+				c = ip->reg;
+			obase = (Optable*)op->proto;
+			goto newop;
+		case FRMEX:	/* Extended FP R/M field with op code (/digit) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			if ((c&0xc0) == 0xc0)
+				c = (c&0x3f)+8;		/* 64-entry table */
+			else
+				c = ip->reg;
+			obase = (Optable*)op->proto;
+			goto newop;
+		case RMR:	/* R/M register only (mod = 11) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if ((c&0xc0) != 0xc0) {
+				werrstr("invalid R/M register: %x", c);
+				return 0;
+			}
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			break;
+		case RMM:	/* R/M register only (mod = 11) */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if ((c&0xc0) == 0xc0) {
+				werrstr("invalid R/M memory mode: %x", c);
+				return 0;
+			}
+			if (modrm(map, ip, c) < 0)
+				return 0;
+			break;
+		case PTR:	/* Seg:Displacement addr (ptr16:16 or ptr16:32) */
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->disp) < 0)
+					return 0;
+			} else {
+				if (igets(map, ip, &s)< 0)
+					return 0;
+				ip->disp = s&0xffff;
+			}
+			if (igets(map, ip, (ushort*)&ip->seg) < 0)
+				return 0;
+			ip->jumptype = PTR;
+			break;
+		case AUXMM:	/* Multi-byte op code; prefix determines table selection */
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			obase = (Optable*)op->proto;
+			switch (ip->opre) {
+			case 0x66:	op = optab660F; break;
+			case 0xF2:	op = optabF20F; break;
+			case 0xF3:	op = optabF30F; break;
+			default:	op = nil; break;
+			}
+			if(op != nil && op[c].proto != nil)
+				obase = op;
+			norex = 1;	/* no more rex prefixes */
+			/* otherwise the optab entry captures it */
+			goto newop;
+		case AUX:	/* Multi-byte op code - Auxiliary table */
+			obase = (Optable*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			goto newop;
+		case OPRE:	/* Instr Prefix or media op */
+			ip->opre = c;
+			/* fall through */
+		case PRE:	/* Instr Prefix */
+			ip->prefix = (char*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (ip->opre && c == 0x0F)
+				ip->prefix = 0;
+			goto newop;
+		case SEG:	/* Segment Prefix */
+			ip->segment = (char*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			goto newop;
+		case OPOVER:	/* Operand size override */
+			ip->opre = c;
+			ip->osize = 'W';
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			if (c == 0x0F)
+				ip->osize = 'L';
+			else if (ip->amd64 && (c&0xF0) == 0x40)
+				ip->osize = 'Q';
+			goto newop;
+		case ADDOVER:	/* Address size override */
+			ip->asize = 0;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			goto newop;
+		case JUMP:	/* mark instruction as JUMP or RET */
+		case RET:
+			ip->jumptype = op->operand[i];
+			break;
+		default:
+			werrstr("bad operand type %d", op->operand[i]);
+			return 0;
+		}
+	}
+	return op;
+}
+
+#pragma	varargck	argpos	bprint		2
+
+static void
+bprint(Instr *ip, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	ip->curr = vseprint(ip->curr, ip->end, fmt, arg);
+	va_end(arg);
+}
+
+/*
+ *  if we want to call 16 bit regs AX,BX,CX,...
+ *  and 32 bit regs EAX,EBX,ECX,... then
+ *  change the defs of ANAME and ONAME to:
+ *  #define	ANAME(ip)	((ip->asize == 'E' ? "E" : "")
+ *  #define	ONAME(ip)	((ip)->osize == 'L' ? "E" : "")
+ */
+#define	ANAME(ip)	""
+#define	ONAME(ip)	""
+
+static char *reg[] =  {
+[AX]	"AX",
+[CX]	"CX",
+[DX]	"DX",
+[BX]	"BX",
+[SP]	"SP",
+[BP]	"BP",
+[SI]	"SI",
+[DI]	"DI",
+
+	/* amd64 */
+[R8]	"R8",
+[R9]	"R9",
+[R10]	"R10",
+[R11]	"R11",
+[R12]	"R12",
+[R13]	"R13",
+[R14]	"R14",
+[R15]	"R15",
+};
+
+static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
+static char *breg64[] = { "AL", "CL", "DL", "BL", "SPB", "BPB", "SIB", "DIB",
+	"R8B", "R9B", "R10B", "R11B", "R12B", "R13B", "R14B", "R15B" };
+static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+static void
+plocal(Instr *ip)
+{
+	int ret;
+	int32 offset;
+	Symbol s;
+	char *reg;
+
+	offset = ip->disp;
+	if (!findsym(ip->addr, CTEXT, &s) || !findlocal(&s, FRAMENAME, &s)) {
+		bprint(ip, "%lux(SP)", offset);
+		return;
+	}
+
+	if (s.value > ip->disp) {
+		ret = getauto(&s, s.value-ip->disp-mach->szaddr, CAUTO, &s);
+		reg = "(SP)";
+	} else {
+		offset -= s.value;
+		ret = getauto(&s, offset, CPARAM, &s);
+		reg = "(FP)";
+	}
+	if (ret)
+		bprint(ip, "%s+", s.name);
+	else
+		offset = ip->disp;
+	bprint(ip, "%lux%s", offset, reg);
+}
+
+static int
+isjmp(Instr *ip)
+{
+	switch(ip->jumptype){
+	case Iwds:
+	case Jbs:
+	case JUMP:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * This is too smart for its own good, but it really is nice
+ * to have accurate translations when debugging, and it
+ * helps us identify which code is different in binaries that
+ * are changed on sources.
+ */
+static int
+issymref(Instr *ip, Symbol *s, int32 w, int32 val)
+{
+	Symbol next, tmp;
+	int32 isstring, size;
+
+	if (isjmp(ip))
+		return 1;
+	if (s->class==CTEXT && w==0)
+		return 1;
+	if (s->class==CDATA) {
+		/* use first bss symbol (or "end") rather than edata */
+		if (s->name[0]=='e' && strcmp(s->name, "edata") == 0){
+			if((s ->index >= 0 && globalsym(&tmp, s->index+1) && tmp.value==s->value)
+			|| (s->index > 0 && globalsym(&tmp, s->index-1) && tmp.value==s->value))
+				*s = tmp;
+		}
+		if (w == 0)
+			return 1;
+		for (next=*s; next.value==s->value; next=tmp)
+			if (!globalsym(&tmp, next.index+1))
+				break;
+		size = next.value - s->value;
+		if (w >= size)
+			return 0;
+		if (w > size-w)
+			w = size-w;
+		/* huge distances are usually wrong except in .string */
+		isstring = (s->name[0]=='.' && strcmp(s->name, ".string") == 0);
+		if (w > 8192 && !isstring)
+			return 0;
+		/* medium distances are tricky - look for constants */
+		/* near powers of two */
+		if ((val&(val-1)) == 0 || (val&(val+1)) == 0)
+			return 0;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+immediate(Instr *ip, vlong val)
+{
+	Symbol s;
+	int32 w;
+
+	if (findsym(val, CANY, &s)) {		/* TO DO */
+		w = val - s.value;
+		if (w < 0)
+			w = -w;
+		if (issymref(ip, &s, w, val)) {
+			if (w)
+				bprint(ip, "%s+%#lux(SB)", s.name, w);
+			else
+				bprint(ip, "%s(SB)", s.name);
+			return;
+		}
+/*
+		if (s.class==CDATA && globalsym(&s, s.index+1)) {
+			w = s.value - val;
+			if (w < 0)
+				w = -w;
+			if (w < 4096) {
+				bprint(ip, "%s-%#lux(SB)", s.name, w);
+				return;
+			}
+		}
+*/
+	}
+	if((ip->rex & REXW) == 0)
+		bprint(ip, "%lux", (long)val);
+	else
+		bprint(ip, "%llux", val);
+}
+
+static void
+pea(Instr *ip)
+{
+	if (ip->mod == 3) {
+		if (ip->osize == 'B')
+			bprint(ip, (ip->rex & REXB? breg64: breg)[(uchar)ip->base]);
+		else if(ip->rex & REXB)
+			bprint(ip, "%s%s", ANAME(ip), reg[ip->base+8]);
+		else
+			bprint(ip, "%s%s", ANAME(ip), reg[(uchar)ip->base]);
+		return;
+	}
+	if (ip->segment)
+		bprint(ip, ip->segment);
+	if (ip->asize == 'E' && ip->base == SP)
+		plocal(ip);
+	else {
+		if (ip->base < 0)
+			immediate(ip, ip->disp);
+		else {
+			bprint(ip, "%lux", ip->disp);
+			if(ip->rip)
+				bprint(ip, "(RIP)");
+			bprint(ip,"(%s%s)", ANAME(ip), reg[ip->rex&REXB? ip->base+8: ip->base]);
+		}
+	}
+	if (ip->index >= 0)
+		bprint(ip,"(%s%s*%d)", ANAME(ip), reg[ip->rex&REXX? ip->index+8: ip->index], 1<<ip->ss);
+}
+
+static void
+prinstr(Instr *ip, char *fmt)
+{
+	vlong v;
+
+	if (ip->prefix)
+		bprint(ip, "%s ", ip->prefix);
+	for (; *fmt && ip->curr < ip->end; fmt++) {
+		if (*fmt != '%'){
+			*ip->curr++ = *fmt;
+			continue;
+		}
+		switch(*++fmt){
+		case '%':
+			*ip->curr++ = '%';
+			break;
+		case 'A':
+			bprint(ip, "%s", ANAME(ip));
+			break;
+		case 'C':
+			bprint(ip, "CR%d", ip->reg);
+			break;
+		case 'D':
+			if (ip->reg < 4 || ip->reg == 6 || ip->reg == 7)
+				bprint(ip, "DR%d",ip->reg);
+			else
+				bprint(ip, "???");
+			break;
+		case 'I':
+			bprint(ip, "$");
+			immediate(ip, ip->imm2);
+			break;
+		case 'O':
+			bprint(ip,"%s", ONAME(ip));
+			break;
+		case 'i':
+			bprint(ip, "$");
+			v = ip->imm;
+			if(ip->rex & REXW)
+				v = ip->imm64;
+			immediate(ip, v);
+			break;
+		case 'R':
+			bprint(ip, "%s%s", ONAME(ip), reg[ip->rex&REXR? ip->reg+8: ip->reg]);
+			break;
+		case 'S':
+			if(ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
+				bprint(ip, "Q");
+			else
+				bprint(ip, "%c", ip->osize);
+			break;
+		case 's':
+			if(ip->opre == 0 || ip->opre == 0x66)
+				bprint(ip, "P");
+			else
+				bprint(ip, "S");
+			if(ip->opre == 0xf2 || ip->opre == 0x66)
+				bprint(ip, "D");
+			else
+				bprint(ip, "S");
+			break;
+		case 'T':
+			if (ip->reg == 6 || ip->reg == 7)
+				bprint(ip, "TR%d",ip->reg);
+			else
+				bprint(ip, "???");
+			break;
+		case 'W':
+			if (ip->osize == 'Q' || ip->osize == 'L' && ip->rex & REXW)
+				bprint(ip, "CDQE");
+			else if (ip->osize == 'L')
+				bprint(ip,"CWDE");
+			else
+				bprint(ip, "CBW");
+			break;
+		case 'd':
+			bprint(ip,"%ux:%lux",ip->seg,ip->disp);
+			break;
+		case 'm':
+			if (ip->mod == 3 && ip->osize != 'B') {
+				if(fmt[1] != '*'){
+					if(ip->opre != 0) {
+						bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
+						break;
+					}
+				} else
+					fmt++;
+				bprint(ip, "M%d", ip->base);
+				break;
+			}
+			pea(ip);
+			break;
+		case 'e':
+			pea(ip);
+			break;
+		case 'f':
+			bprint(ip, "F%d", ip->base);
+			break;
+		case 'g':
+			if (ip->reg < 6)
+				bprint(ip,"%s",sreg[ip->reg]);
+			else
+				bprint(ip,"???");
+			break;
+		case 'p':
+			/*
+			 * signed immediate in the uint32 ip->imm.
+			 */
+			v = (int32)ip->imm;
+			immediate(ip, v+ip->addr+ip->n);
+			break;
+		case 'r':
+			if (ip->osize == 'B')
+				bprint(ip,"%s", (ip->rex? breg64: breg)[ip->rex&REXR? ip->reg+8: ip->reg]);
+			else
+				bprint(ip, reg[ip->rex&REXR? ip->reg+8: ip->reg]);
+			break;
+		case 'w':
+			if (ip->osize == 'Q' || ip->rex & REXW)
+				bprint(ip, "CQO");
+			else if (ip->osize == 'L')
+				bprint(ip,"CDQ");
+			else
+				bprint(ip, "CWD");
+			break;
+		case 'M':
+			if(ip->opre != 0)
+				bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
+			else
+				bprint(ip, "M%d", ip->reg);
+			break;
+		case 'x':
+			if (ip->mod == 3 && ip->osize != 'B') {
+				bprint(ip, "X%d", ip->rex&REXB? ip->base+8: ip->base);
+				break;
+			}
+			pea(ip);
+			break;
+		case 'X':
+			bprint(ip, "X%d", ip->rex&REXR? ip->reg+8: ip->reg);
+			break;
+		default:
+			bprint(ip, "%%%c", *fmt);
+			break;
+		}
+	}
+	*ip->curr = 0;		/* there's always room for 1 byte */
+}
+
+static int
+i386inst(Map *map, uvlong pc, char modifier, char *buf, int n)
+{
+	Instr instr;
+	Optable *op;
+
+	USED(modifier);
+	op = mkinstr(map, &instr, pc);
+	if (op == 0) {
+		errstr(buf, n);
+		return -1;
+	}
+	instr.curr = buf;
+	instr.end = buf+n-1;
+	prinstr(&instr, op->proto);
+	return instr.n;
+}
+
+static int
+i386das(Map *map, uvlong pc, char *buf, int n)
+{
+	Instr instr;
+	int i;
+
+	if (mkinstr(map, &instr, pc) == 0) {
+		errstr(buf, n);
+		return -1;
+	}
+	for(i = 0; i < instr.n && n > 2; i++) {
+		_hexify(buf, instr.mem[i], 1);
+		buf += 2;
+		n -= 2;
+	}
+	*buf = 0;
+	return instr.n;
+}
+
+static int
+i386instlen(Map *map, uvlong pc)
+{
+	Instr i;
+
+	if (mkinstr(map, &i, pc))
+		return i.n;
+	return -1;
+}
+
+static int
+i386foll(Map *map, uvlong pc, Rgetter rget, uvlong *foll)
+{
+	Instr i;
+	Optable *op;
+	ushort s;
+	uvlong l, addr;
+	vlong v;
+	int n;
+
+	op = mkinstr(map, &i, pc);
+	if (!op)
+		return -1;
+
+	n = 0;
+
+	switch(i.jumptype) {
+	case RET:		/* RETURN or LEAVE */
+	case Iw:		/* RETURN */
+		if (strcmp(op->proto, "LEAVE") == 0) {
+			if (geta(map, (*rget)(map, "BP"), &l) < 0)
+				return -1;
+		} else if (geta(map, (*rget)(map, mach->sp), &l) < 0)
+			return -1;
+		foll[0] = l;
+		return 1;
+	case Iwds:		/* pc relative JUMP or CALL*/
+	case Jbs:		/* pc relative JUMP or CALL */
+		v = (int32)i.imm;
+		foll[0] = pc+v+i.n;
+		n = 1;
+		break;
+	case PTR:		/* seg:displacement JUMP or CALL */
+		foll[0] = (i.seg<<4)+i.disp;
+		return 1;
+	case JUMP:		/* JUMP or CALL EA */
+
+		if(i.mod == 3) {
+			foll[0] = (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]);
+			return 1;
+		}
+			/* calculate the effective address */
+		addr = i.disp;
+		if (i.base >= 0) {
+			if (geta(map, (*rget)(map, reg[i.rex&REXB? i.base+8: i.base]), &l) < 0)
+				return -1;
+			addr += l;
+		}
+		if (i.index >= 0) {
+			if (geta(map, (*rget)(map, reg[i.rex&REXX? i.index+8: i.index]), &l) < 0)
+				return -1;
+			addr += l*(1<<i.ss);
+		}
+			/* now retrieve a seg:disp value at that address */
+		if (get2(map, addr, &s) < 0)			/* seg */
+			return -1;
+		foll[0] = s<<4;
+		addr += 2;
+		if (i.asize == 'L') {
+			if (geta(map, addr, &l) < 0)		/* disp32 */
+				return -1;
+			foll[0] += l;
+		} else {					/* disp16 */
+			if (get2(map, addr, &s) < 0)
+				return -1;
+			foll[0] += s;
+		}
+		return 1;
+	default:
+		break;
+	}
+	if (strncmp(op->proto,"JMP", 3) == 0 || strncmp(op->proto,"CALL", 4) == 0)
+		return 1;
+	foll[n++] = pc+i.n;
+	return n;
+}
diff --git a/src/libmach/8obj.c b/src/libmach/8obj.c
new file mode 100644
index 0000000..f8ca1c4
--- /dev/null
+++ b/src/libmach/8obj.c
@@ -0,0 +1,175 @@
+// Inferno libmach/8obj.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/8obj.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.
+//	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+//	Portions Copyright © 1997-1999 Vita Nuova Limited.
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+//	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * 8obj.c - identify and parse a 386 object file
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "../cmd/8l/8.out.h"
+#include "obj.h"
+
+typedef struct Addr	Addr;
+struct Addr
+{
+	char	sym;
+	char	flags;
+	char gotype;
+};
+static	Addr	addr(Biobuf*);
+static	char	type2char(int);
+static	void	skip(Biobuf*, int);
+
+int
+_is8(char *t)
+{
+	uchar *s = (uchar*)t;
+
+	return  s[0] == (ANAME&0xff)			/* aslo = ANAME */
+		&& s[1] == ((ANAME>>8)&0xff)
+		&& s[2] == D_FILE			/* type */
+		&& s[3] == 1				/* sym */
+		&& s[4] == '<';				/* name of file */
+}
+
+int
+_read8(Biobuf *bp, Prog* p)
+{
+	int as, n, c;
+	Addr a;
+
+	as = Bgetc(bp);		/* as(low) */
+	if(as < 0)
+		return 0;
+	c = Bgetc(bp);		/* as(high) */
+	if(c < 0)
+		return 0;
+	as |= ((c & 0xff) << 8);
+	p->kind = aNone;
+	p->sig = 0;
+	if(as == ANAME || as == ASIGNAME){
+		if(as == ASIGNAME){
+			Bread(bp, &p->sig, 4);
+			p->sig = leswal(p->sig);
+		}
+		p->kind = aName;
+		p->type = type2char(Bgetc(bp));		/* type */
+		p->sym = Bgetc(bp);			/* sym */
+		n = 0;
+		for(;;) {
+			as = Bgetc(bp);
+			if(as < 0)
+				return 0;
+			n++;
+			if(as == 0)
+				break;
+		}
+		p->id = malloc(n);
+		if(p->id == 0)
+			return 0;
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->id, n) != n)
+			return 0;
+		return 1;
+	}
+	if(as == ATEXT)
+		p->kind = aText;
+	if(as == AGLOBL)
+		p->kind = aData;
+	skip(bp, 4);		/* lineno(4) */
+	a = addr(bp);
+	addr(bp);
+	if(!(a.flags & T_SYM))
+		p->kind = aNone;
+	p->sym = a.sym;
+	return 1;
+}
+
+static Addr
+addr(Biobuf *bp)
+{
+	Addr a;
+	int t;
+	long off;
+
+	off = 0;
+	a.sym = -1;
+	a.flags = Bgetc(bp);			/* flags */
+	if(a.flags & T_INDEX)
+		skip(bp, 2);
+	if(a.flags & T_OFFSET){
+		off = Bgetc(bp);
+		off |= Bgetc(bp) << 8;
+		off |= Bgetc(bp) << 16;
+		off |= Bgetc(bp) << 24;
+		if(off < 0)
+			off = -off;
+	}
+	if(a.flags & T_OFFSET2){
+		Bgetc(bp);
+		Bgetc(bp);
+		Bgetc(bp);
+		Bgetc(bp);
+	}
+	if(a.flags & T_SYM)
+		a.sym = Bgetc(bp);
+	if(a.flags & T_FCONST)
+		skip(bp, 8);
+	else
+	if(a.flags & T_SCONST)
+		skip(bp, NSNAME);
+	if(a.flags & T_TYPE) {
+		t = Bgetc(bp);
+		if(a.sym > 0 && (t==D_PARAM || t==D_AUTO))
+			_offset(a.sym, off);
+	}
+	if(a.flags & T_GOTYPE)
+		a.gotype = Bgetc(bp);
+	return a;
+}
+
+static char
+type2char(int t)
+{
+	switch(t){
+	case D_EXTERN:		return 'U';
+	case D_STATIC:		return 'b';
+	case D_AUTO:		return 'a';
+	case D_PARAM:		return 'p';
+	default:		return UNKNOWN;
+	}
+}
+
+static void
+skip(Biobuf *bp, int n)
+{
+	while (n-- > 0)
+		Bgetc(bp);
+}
diff --git a/src/libmach/Makefile b/src/libmach/Makefile
new file mode 100644
index 0000000..4a5e340
--- /dev/null
+++ b/src/libmach/Makefile
@@ -0,0 +1,65 @@
+# Derived from Inferno libmach/mkfile
+# http://code.google.com/p/inferno-os/source/browse/utils/libmach/mkfile
+#
+#	Copyright © 1994-1999 Lucent Technologies Inc.
+#	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+#	Portions Copyright © 1997-1999 Vita Nuova Limited.
+#	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+#	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+#	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+include ../Make.conf
+
+LIB=libmach.a
+OFILES=\
+	executable.$O\
+	fakeobj.$O\
+	map.$O\
+	obj.$O\
+	swap.$O\
+	sym.$O\
+	access.$O\
+	machdata.$O\
+	setmach.$O\
+	6.$O\
+	8.$O\
+	8db.$O\
+	5obj.$O\
+	6obj.$O\
+	8obj.$O\
+	$(GOOS).$O\
+
+HFILES=$(GOROOT)/include/mach.h elf.h macho.h obj.h
+
+install: $(LIB)
+	cp $(LIB) $(GOROOT)/lib
+
+$(LIB): $(OFILES)
+	ar rsc $(LIB) $(OFILES)
+
+$(OFILES): $(HFILES)
+
+clean:
+	rm -f *.$O $(LIB)
+
+nuke: clean
+	rm -f $(GOROOT)/lib/$(LIB)
+
diff --git a/src/libmach/access.c b/src/libmach/access.c
new file mode 100644
index 0000000..0ee75d1
--- /dev/null
+++ b/src/libmach/access.c
@@ -0,0 +1,241 @@
+// Inferno libmach/access.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/access.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * functions to read and write an executable or file image
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+static	int	mget(Map*, uvlong, void*, int);
+static	int	mput(Map*, uvlong, void*, int);
+static	Seg*	reloc(Map*, uvlong, vlong*);
+
+/*
+ * routines to get/put various types
+ */
+int
+geta(Map *map, uvlong addr, uvlong *x)
+{
+	uint32 l;
+	uvlong vl;
+
+	if (mach->szaddr == 8){
+		if (get8(map, addr, &vl) < 0)
+			return -1;
+		*x = vl;
+		return 1;
+	}
+
+	if (get4(map, addr, &l) < 0)
+		return -1;
+	*x = l;
+
+	return 1;
+}
+
+int
+get8(Map *map, uvlong addr, uvlong *x)
+{
+	if (!map) {
+		werrstr("get8: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		*x = addr;
+		return 1;
+	}
+	if (mget(map, addr, x, 8) < 0)
+		return -1;
+	*x = machdata->swav(*x);
+	return 1;
+}
+
+int
+get4(Map *map, uvlong addr, uint32 *x)
+{
+	if (!map) {
+		werrstr("get4: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		*x = addr;
+		return 1;
+	}
+	if (mget(map, addr, x, 4) < 0)
+		return -1;
+	*x = machdata->swal(*x);
+	return 1;
+}
+
+int
+get2(Map *map, uvlong addr, ushort *x)
+{
+	if (!map) {
+		werrstr("get2: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		*x = addr;
+		return 1;
+	}
+	if (mget(map, addr, x, 2) < 0)
+		return -1;
+	*x = machdata->swab(*x);
+	return 1;
+}
+
+int
+get1(Map *map, uvlong addr, uchar *x, int size)
+{
+	uchar *cp;
+
+	if (!map) {
+		werrstr("get1: invalid map");
+		return -1;
+	}
+
+	if (map->nsegs == 1 && map->seg[0].fd < 0) {
+		cp = (uchar*)&addr;
+		while (cp < (uchar*)(&addr+1) && size-- > 0)
+			*x++ = *cp++;
+		while (size-- > 0)
+			*x++ = 0;
+	} else
+		return mget(map, addr, x, size);
+	return 1;
+}
+
+int
+puta(Map *map, uvlong addr, uvlong v)
+{
+	if (mach->szaddr == 8)
+		return put8(map, addr, v);
+
+	return put4(map, addr, v);
+}
+
+int
+put8(Map *map, uvlong addr, uvlong v)
+{
+	if (!map) {
+		werrstr("put8: invalid map");
+		return -1;
+	}
+	v = machdata->swav(v);
+	return mput(map, addr, &v, 8);
+}
+
+int
+put4(Map *map, uvlong addr, uint32 v)
+{
+	if (!map) {
+		werrstr("put4: invalid map");
+		return -1;
+	}
+	v = machdata->swal(v);
+	return mput(map, addr, &v, 4);
+}
+
+int
+put2(Map *map, uvlong addr, ushort v)
+{
+	if (!map) {
+		werrstr("put2: invalid map");
+		return -1;
+	}
+	v = machdata->swab(v);
+	return mput(map, addr, &v, 2);
+}
+
+int
+put1(Map *map, uvlong addr, uchar *v, int size)
+{
+	if (!map) {
+		werrstr("put1: invalid map");
+		return -1;
+	}
+	return mput(map, addr, v, size);
+}
+
+static int
+mget(Map *map, uvlong addr, void *buf, int size)
+{
+	uvlong off;
+	Seg *s;
+
+	s = reloc(map, addr, (vlong*)&off);
+	if (!s)
+		return -1;
+	if (s->rw == nil) {
+		werrstr("unreadable map");
+		return -1;
+	}
+	return s->rw(map, s, off, buf, size, 1);
+}
+
+static int
+mput(Map *map, uvlong addr, void *buf, int size)
+{
+	vlong off;
+	Seg *s;
+
+	s = reloc(map, addr, &off);
+	if (!s)
+		return -1;
+	if (s->rw == nil) {
+		werrstr("unwritable map");
+		return -1;
+	}
+	return s->rw(map, s, off, buf, size, 0);
+}
+
+/*
+ *	convert address to file offset; returns nonzero if ok
+ */
+static Seg*
+reloc(Map *map, uvlong addr, vlong *offp)
+{
+	int i;
+
+	for (i = 0; i < map->nsegs; i++) {
+		if (map->seg[i].inuse)
+		if (map->seg[i].b <= addr && addr < map->seg[i].e) {
+			*offp = addr + map->seg[i].f - map->seg[i].b;
+			return &map->seg[i];
+		}
+	}
+	werrstr("can't translate address %llux", addr);
+	return 0;
+}
diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c
new file mode 100644
index 0000000..87fe423
--- /dev/null
+++ b/src/libmach/darwin.c
@@ -0,0 +1,869 @@
+//	Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#define __DARWIN_UNIX03 0
+
+#include <u.h>
+#include <sys/ptrace.h>
+#include <sys/signal.h>
+#include <mach/mach.h>
+#include <mach/mach_traps.h>
+#include <errno.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#define Ureg Ureg32
+#include <ureg_x86.h>
+#undef Ureg
+#define Ureg Ureg64
+#include <ureg_amd64.h>
+#undef Ureg
+#undef waitpid	/* want Unix waitpid, not Plan 9 */
+
+typedef struct Ureg32 Ureg32;
+typedef struct Ureg64 Ureg64;
+
+extern mach_port_t mach_reply_port(void);	// should be in system headers, is not
+
+// Mach-error wrapper.
+// Takes a mach return code and converts it into 0 / -1,
+// setting errstr when it returns -1.
+
+static struct {
+	int code;
+	char *name;
+} macherr[] = {
+	KERN_INVALID_ADDRESS,	"invalid address",
+	KERN_PROTECTION_FAILURE,	"protection failure",
+	KERN_NO_SPACE,	"no space",
+	KERN_INVALID_ARGUMENT,	"invalid argument",
+	KERN_FAILURE,	"failure",
+	KERN_RESOURCE_SHORTAGE,	"resource shortage",
+	KERN_NOT_RECEIVER,	"not receiver",
+	KERN_NO_ACCESS,	"no access",
+	KERN_MEMORY_FAILURE,	"memory failure",
+	KERN_MEMORY_ERROR,	"memory error",
+	KERN_ALREADY_IN_SET,	"already in set",
+	KERN_NOT_IN_SET,	"not in set",
+	KERN_NAME_EXISTS,	"name exists",
+	KERN_ABORTED,	"aborted",
+	KERN_INVALID_NAME,	"invalid name",
+	KERN_INVALID_TASK,	"invalid task",
+	KERN_INVALID_RIGHT,	"invalid right",
+	KERN_INVALID_VALUE,	"invalid value",
+	KERN_UREFS_OVERFLOW,	"urefs overflow",
+	KERN_INVALID_CAPABILITY,	"invalid capability",
+	KERN_RIGHT_EXISTS,	"right exists",
+	KERN_INVALID_HOST,	"invalid host",
+	KERN_MEMORY_PRESENT,	"memory present",
+	KERN_MEMORY_DATA_MOVED,	"memory data moved",
+	KERN_MEMORY_RESTART_COPY,	"memory restart copy",
+	KERN_INVALID_PROCESSOR_SET,	"invalid processor set",
+	KERN_POLICY_LIMIT,	"policy limit",
+	KERN_INVALID_POLICY,	"invalid policy",
+	KERN_INVALID_OBJECT,	"invalid object",
+	KERN_ALREADY_WAITING,	"already waiting",
+	KERN_DEFAULT_SET,	"default set",
+	KERN_EXCEPTION_PROTECTED,	"exception protected",
+	KERN_INVALID_LEDGER,	"invalid ledger",
+	KERN_INVALID_MEMORY_CONTROL,	"invalid memory control",
+	KERN_INVALID_SECURITY,	"invalid security",
+	KERN_NOT_DEPRESSED,	"not depressed",
+	KERN_TERMINATED,	"terminated",
+	KERN_LOCK_SET_DESTROYED,	"lock set destroyed",
+	KERN_LOCK_UNSTABLE,	"lock unstable",
+	KERN_LOCK_OWNED,	"lock owned",
+	KERN_LOCK_OWNED_SELF,	"lock owned self",
+	KERN_SEMAPHORE_DESTROYED,	"semaphore destroyed",
+	KERN_RPC_SERVER_TERMINATED,	"rpc server terminated",
+	KERN_RPC_TERMINATE_ORPHAN,	"rpc terminate orphan",
+	KERN_RPC_CONTINUE_ORPHAN,	"rpc continue orphan",
+	KERN_NOT_SUPPORTED,	"not supported",
+	KERN_NODE_DOWN,	"node down",
+	KERN_NOT_WAITING,	"not waiting",
+	KERN_OPERATION_TIMED_OUT,	"operation timed out",
+	KERN_RETURN_MAX,	"return max",
+
+	MACH_SEND_IN_PROGRESS,	"send in progress",
+	MACH_SEND_INVALID_DATA,	"send invalid data",
+	MACH_SEND_INVALID_DEST,	"send invalid dest",
+	MACH_SEND_TIMED_OUT,	"send timed out",
+	MACH_SEND_INTERRUPTED,	"send interrupted",
+	MACH_SEND_MSG_TOO_SMALL,	"send msg too small",
+	MACH_SEND_INVALID_REPLY,	"send invalid reply",
+	MACH_SEND_INVALID_RIGHT,	"send invalid right",
+	MACH_SEND_INVALID_NOTIFY,	"send invalid notify",
+	MACH_SEND_INVALID_MEMORY,	"send invalid memory",
+	MACH_SEND_NO_BUFFER,	"send no buffer",
+	MACH_SEND_TOO_LARGE,	"send too large",
+	MACH_SEND_INVALID_TYPE,	"send invalid type",
+	MACH_SEND_INVALID_HEADER,	"send invalid header",
+	MACH_SEND_INVALID_TRAILER,	"send invalid trailer",
+	MACH_SEND_INVALID_RT_OOL_SIZE,	"send invalid rt ool size",
+	MACH_RCV_IN_PROGRESS,	"rcv in progress",
+	MACH_RCV_INVALID_NAME,	"rcv invalid name",
+	MACH_RCV_TIMED_OUT,	"rcv timed out",
+	MACH_RCV_TOO_LARGE,	"rcv too large",
+	MACH_RCV_INTERRUPTED,	"rcv interrupted",
+	MACH_RCV_PORT_CHANGED,	"rcv port changed",
+	MACH_RCV_INVALID_NOTIFY,	"rcv invalid notify",
+	MACH_RCV_INVALID_DATA,	"rcv invalid data",
+	MACH_RCV_PORT_DIED,	"rcv port died",
+	MACH_RCV_IN_SET,	"rcv in set",
+	MACH_RCV_HEADER_ERROR,	"rcv header error",
+	MACH_RCV_BODY_ERROR,	"rcv body error",
+	MACH_RCV_INVALID_TYPE,	"rcv invalid type",
+	MACH_RCV_SCATTER_SMALL,	"rcv scatter small",
+	MACH_RCV_INVALID_TRAILER,	"rcv invalid trailer",
+	MACH_RCV_IN_PROGRESS_TIMED,	"rcv in progress timed",
+
+	MIG_TYPE_ERROR,	"mig type error",
+	MIG_REPLY_MISMATCH,	"mig reply mismatch",
+	MIG_REMOTE_ERROR,	"mig remote error",
+	MIG_BAD_ID,	"mig bad id",
+	MIG_BAD_ARGUMENTS,	"mig bad arguments",
+	MIG_NO_REPLY,	"mig no reply",
+	MIG_EXCEPTION,	"mig exception",
+	MIG_ARRAY_TOO_LARGE,	"mig array too large",
+	MIG_SERVER_DIED,	"server died",
+	MIG_TRAILER_ERROR,	"trailer has an unknown format",
+};
+
+static int
+me(kern_return_t r)
+{
+	int i;
+
+	if(r == 0)
+		return 0;
+
+	for(i=0; i<nelem(macherr); i++){
+		if(r == macherr[i].code){
+			werrstr("%s", macherr[i].name);
+			return -1;
+		}
+	}
+	werrstr("mach error %#x", r);
+	return -1;
+}
+
+// Plan 9 and Linux do not distinguish between
+// process ids and thread ids, so the interface here doesn't either.
+// Unfortunately, Mach has three kinds of identifiers: process ids,
+// handles to tasks (processes), and handles to threads within a
+// process.  All of them are small integers.
+//
+// To accomodate Mach, we employ a clumsy hack: in this interface,
+// if you pass in a positive number, that's a process id.
+// If you pass in a negative number, that identifies a thread that
+// has been previously returned by procthreadpids (it indexes
+// into the Thread table below).
+
+// Table of threads we have handles for.
+typedef struct Thread Thread;
+struct Thread
+{
+	int pid;
+	mach_port_t task;
+	mach_port_t thread;
+	int stopped;
+	int exc;
+	int code[10];
+	Map *map;
+};
+static Thread thr[1000];
+static int nthr;
+static pthread_mutex_t mu;
+static pthread_cond_t cond;
+static void* excthread(void*);
+static void* waitthread(void*);
+static mach_port_t excport;
+
+enum {
+	ExcMask = EXC_MASK_BAD_ACCESS |
+		EXC_MASK_BAD_INSTRUCTION |
+		EXC_MASK_ARITHMETIC |
+		EXC_MASK_BREAKPOINT |
+		EXC_MASK_SOFTWARE
+};
+
+// Add process pid to the thread table.
+// If it's already there, don't re-add it (unless force != 0).
+static Thread*
+addpid(int pid, int force)
+{
+	int i, j;
+	mach_port_t task;
+	mach_port_t *thread;
+	uint nthread;
+	Thread *ret;
+	static int first = 1;
+
+	if(first){
+		// Allocate a port for exception messages and
+		// send all thread exceptions to that port.
+		// The excthread reads that port and signals
+		// us if we are waiting on that thread.
+		pthread_t p;
+
+		excport = mach_reply_port();
+		pthread_mutex_init(&mu, nil);
+		pthread_cond_init(&cond, nil);
+		pthread_create(&p, nil, excthread, nil);
+		pthread_create(&p, nil, waitthread, (void*)(uintptr)pid);
+		first = 0;
+	}
+
+	if(!force){
+		for(i=0; i<nthr; i++)
+			if(thr[i].pid == pid)
+				return &thr[i];
+	}
+	if(me(task_for_pid(mach_task_self(), pid, &task)) < 0)
+		return nil;
+	if(me(task_threads(task, &thread, &nthread)) < 0)
+		return nil;
+	mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND);
+	if(me(task_set_exception_ports(task, ExcMask,
+			excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){
+		fprint(2, "warning: cannot set excport: %r\n");
+	}
+	ret = nil;
+	for(j=0; j<nthread; j++){
+		if(force){
+			// If we're forcing a refresh, don't re-add existing threads.
+			for(i=0; i<nthr; i++)
+				if(thr[i].pid == pid && thr[i].thread == thread[j]){
+					if(ret == nil)
+						ret = &thr[i];
+					goto skip;
+				}
+		}
+		if(nthr >= nelem(thr))
+			return nil;
+		// TODO: We probably should save the old thread exception
+		// ports for each bit and then put them back when we exit.
+		// Probably the BSD signal handlers have put stuff there.
+		mach_port_insert_right(mach_task_self(), excport, excport, MACH_MSG_TYPE_MAKE_SEND);
+		if(me(thread_set_exception_ports(thread[j], ExcMask,
+				excport, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)) < 0){
+			fprint(2, "warning: cannot set excport: %r\n");
+		}
+		thr[nthr].pid = pid;
+		thr[nthr].task = task;
+		thr[nthr].thread = thread[j];
+		if(ret == nil)
+			ret = &thr[nthr];
+		nthr++;
+	skip:;
+	}
+	return ret;
+}
+
+static Thread*
+idtotable(int id)
+{
+	if(id >= 0)
+		return addpid(id, 1);
+
+	id = -(id+1);
+	if(id >= nthr)
+		return nil;
+	return &thr[id];
+}
+
+/*
+static int
+idtopid(int id)
+{
+	Thread *t;
+
+	if((t = idtotable(id)) == nil)
+		return -1;
+	return t->pid;
+}
+*/
+
+static mach_port_t
+idtotask(int id)
+{
+	Thread *t;
+
+	if((t = idtotable(id)) == nil)
+		return -1;
+	return t->task;
+}
+
+static mach_port_t
+idtothread(int id)
+{
+	Thread *t;
+
+	if((t = idtotable(id)) == nil)
+		return -1;
+	return t->thread;
+}
+
+static int machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr);
+static int machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr);
+
+Map*
+attachproc(int id, Fhdr *fp)
+{
+	Thread *t;
+	Map *map;
+
+	if((t = idtotable(id)) == nil)
+		return nil;
+	if(t->map)
+		return t->map;
+	map = newmap(0, 4);
+	if(!map)
+		return nil;
+	map->pid = -((t-thr) + 1);
+	if(mach->regsize)
+		setmap(map, -1, 0, mach->regsize, 0, "regs", machregrw);
+	setmap(map, -1, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "*text", machsegrw);
+	setmap(map, -1, fp->dataddr, mach->utop, fp->dataddr, "*data", machsegrw);
+	t->map = map;
+	return map;
+}
+
+// Return list of ids for threads in id.
+int
+procthreadpids(int id, int *out, int nout)
+{
+	Thread *t;
+	int i, n, pid;
+
+	t = idtotable(id);
+	if(t == nil)
+		return -1;
+	pid = t->pid;
+	addpid(pid, 1);	// force refresh of thread list
+	n = 0;
+	for(i=0; i<nthr; i++) {
+		if(thr[i].pid == pid) {
+			if(n < nout)
+				out[n] = -(i+1);
+			n++;
+		}
+	}
+	return n;
+}
+
+// Detach from proc.
+// TODO(rsc): Perhaps should unsuspend any threads and clean-up the table.
+void
+detachproc(Map *m)
+{
+	free(m);
+}
+
+// Should return array of pending signals (notes)
+// but don't know how to do that on OS X.
+int
+procnotes(int pid, char ***pnotes)
+{
+	*pnotes = 0;
+	return 0;
+}
+
+// There must be a way to do this.  Gdb can do it.
+// But I don't see, in the Apple gdb sources, how.
+char*
+proctextfile(int pid)
+{
+	return nil;
+}
+
+// Read/write from a Mach data segment.
+static int
+machsegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+	mach_port_t task;
+	int r;
+
+	task = idtotask(map->pid);
+	if(task == -1)
+		return -1;
+
+	if(isr){
+		vm_size_t nn;
+		nn = n;
+		if(me(vm_read_overwrite(task, addr, n, (uintptr)v, &nn)) < 0)
+			return -1;
+		return nn;
+	}else{
+		r = vm_write(task, addr, (uintptr)v, n);
+		if(r == KERN_INVALID_ADDRESS){
+			// Happens when writing to text segment.
+			// Change protections.
+			if(me(vm_protect(task, addr, n, 0, VM_PROT_WRITE|VM_PROT_READ|VM_PROT_EXECUTE)) < 0){
+				fprint(2, "vm_protect: %s\n", r);
+				return -1;
+			}
+			r = vm_write(task, addr, (uintptr)v, n);
+		}
+		if(r != 0){
+			me(r);
+			return -1;
+		}
+		return n;
+	}
+}
+
+// Convert Ureg offset to x86_thread_state32_t offset.
+static int
+go2darwin32(uvlong addr)
+{
+	switch(addr){
+	case offsetof(Ureg32, ax):
+		return offsetof(x86_thread_state32_t, eax);
+	case offsetof(Ureg32, bx):
+		return offsetof(x86_thread_state32_t, ebx);
+	case offsetof(Ureg32, cx):
+		return offsetof(x86_thread_state32_t, ecx);
+	case offsetof(Ureg32, dx):
+		return offsetof(x86_thread_state32_t, edx);
+	case offsetof(Ureg32, si):
+		return offsetof(x86_thread_state32_t, esi);
+	case offsetof(Ureg32, di):
+		return offsetof(x86_thread_state32_t, edi);
+	case offsetof(Ureg32, bp):
+		return offsetof(x86_thread_state32_t, ebp);
+	case offsetof(Ureg32, fs):
+		return offsetof(x86_thread_state32_t, fs);
+	case offsetof(Ureg32, gs):
+		return offsetof(x86_thread_state32_t, gs);
+	case offsetof(Ureg32, pc):
+		return offsetof(x86_thread_state32_t, eip);
+	case offsetof(Ureg32, cs):
+		return offsetof(x86_thread_state32_t, cs);
+	case offsetof(Ureg32, flags):
+		return offsetof(x86_thread_state32_t, eflags);
+	case offsetof(Ureg32, sp):
+		return offsetof(x86_thread_state32_t, esp);
+	}
+	return -1;
+}
+
+// Convert Ureg offset to x86_thread_state64_t offset.
+static int
+go2darwin64(uvlong addr)
+{
+	switch(addr){
+	case offsetof(Ureg64, ax):
+		return offsetof(x86_thread_state64_t, rax);
+	case offsetof(Ureg64, bx):
+		return offsetof(x86_thread_state64_t, rbx);
+	case offsetof(Ureg64, cx):
+		return offsetof(x86_thread_state64_t, rcx);
+	case offsetof(Ureg64, dx):
+		return offsetof(x86_thread_state64_t, rdx);
+	case offsetof(Ureg64, si):
+		return offsetof(x86_thread_state64_t, rsi);
+	case offsetof(Ureg64, di):
+		return offsetof(x86_thread_state64_t, rdi);
+	case offsetof(Ureg64, bp):
+		return offsetof(x86_thread_state64_t, rbp);
+	case offsetof(Ureg64, r8):
+		return offsetof(x86_thread_state64_t, r8);
+	case offsetof(Ureg64, r9):
+		return offsetof(x86_thread_state64_t, r9);
+	case offsetof(Ureg64, r10):
+		return offsetof(x86_thread_state64_t, r10);
+	case offsetof(Ureg64, r11):
+		return offsetof(x86_thread_state64_t, r11);
+	case offsetof(Ureg64, r12):
+		return offsetof(x86_thread_state64_t, r12);
+	case offsetof(Ureg64, r13):
+		return offsetof(x86_thread_state64_t, r13);
+	case offsetof(Ureg64, r14):
+		return offsetof(x86_thread_state64_t, r14);
+	case offsetof(Ureg64, r15):
+		return offsetof(x86_thread_state64_t, r15);
+	case offsetof(Ureg64, fs):
+		return offsetof(x86_thread_state64_t, fs);
+	case offsetof(Ureg64, gs):
+		return offsetof(x86_thread_state64_t, gs);
+	case offsetof(Ureg64, ip):
+		return offsetof(x86_thread_state64_t, rip);
+	case offsetof(Ureg64, cs):
+		return offsetof(x86_thread_state64_t, cs);
+	case offsetof(Ureg64, flags):
+		return offsetof(x86_thread_state64_t, rflags);
+	case offsetof(Ureg64, sp):
+		return offsetof(x86_thread_state64_t, rsp);
+	}
+	return -1;
+}
+
+extern Mach mi386;
+
+// Read/write from fake register segment.
+static int
+machregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+	uint nn, count, state;
+	mach_port_t thread;
+	int reg;
+	char buf[100];
+	union {
+		x86_thread_state64_t reg64;
+		x86_thread_state32_t reg32;
+		uchar p[1];
+	} u;
+	uchar *p;
+
+	if(n > 8){
+		werrstr("asked for %d-byte register", n);
+		return -1;
+	}
+
+	thread = idtothread(map->pid);
+	if(thread == -1){
+		werrstr("no such id");
+		return -1;
+	}
+
+	if(mach == &mi386) {
+		count = x86_THREAD_STATE32_COUNT;
+		state = x86_THREAD_STATE32;
+		if((reg = go2darwin32(addr)) < 0 || reg+n > sizeof u){
+			if(isr){
+				memset(v, 0, n);
+				return 0;
+			}
+			werrstr("register %llud not available", addr);
+			return -1;
+		}
+	} else {
+		count = x86_THREAD_STATE64_COUNT;
+		state = x86_THREAD_STATE64;
+		if((reg = go2darwin64(addr)) < 0 || reg+n > sizeof u){
+			if(isr){
+				memset(v, 0, n);
+				return 0;
+			}
+			werrstr("register %llud not available", addr);
+			return -1;
+		}
+	}
+
+	if(!isr && me(thread_suspend(thread)) < 0){
+		werrstr("thread suspend %#x: %r", thread);
+		return -1;
+	}
+	nn = count;
+	if(me(thread_get_state(thread, state, (void*)u.p, &nn)) < 0){
+		if(!isr)
+			thread_resume(thread);
+		rerrstr(buf, sizeof buf);
+		if(strcmp(buf, "send invalid dest") == 0)
+			werrstr("process exited");
+		else
+			werrstr("thread_get_state: %r");
+		return -1;
+	}
+
+	p = u.p+reg;
+	if(isr)
+		memmove(v, p, n);
+	else{
+		memmove(p, v, n);
+		nn = count;
+		if(me(thread_set_state(thread, state, (void*)u.p, nn)) < 0){
+			thread_resume(thread);
+			werrstr("thread_set_state: %r");
+			return -1;
+		}
+
+		if(me(thread_resume(thread)) < 0){
+			werrstr("thread_resume: %r");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+enum
+{
+	FLAGS_TF = 0x100		// x86 single-step processor flag
+};
+
+// Is thread t suspended?
+static int
+threadstopped(Thread *t)
+{
+	struct thread_basic_info info;
+	uint size;
+
+	size = sizeof info;
+	if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &size)) <  0){
+		fprint(2, "threadstopped thread_info %#x: %r\n");
+		return 1;
+	}
+	return info.suspend_count > 0;
+}
+
+// If thread t is suspended, start it up again.
+// If singlestep is set, only let it execute one instruction.
+static int
+threadstart(Thread *t, int singlestep)
+{
+	int i;
+	uint n;
+	struct thread_basic_info info;
+
+	if(!threadstopped(t))
+		return 0;
+
+	// Set or clear the processor single-step flag, as appropriate.
+	if(mach == &mi386) {
+		x86_thread_state32_t regs;
+		n = x86_THREAD_STATE32_COUNT;
+		if(me(thread_get_state(t->thread, x86_THREAD_STATE32,
+				(thread_state_t)&regs,
+				&n)) < 0)
+			return -1;
+		if(singlestep)
+			regs.eflags |= FLAGS_TF;
+		else
+			regs.eflags &= ~FLAGS_TF;
+		if(me(thread_set_state(t->thread, x86_THREAD_STATE32,
+				(thread_state_t)&regs,
+				x86_THREAD_STATE32_COUNT)) < 0)
+			return -1;
+	} else {
+		x86_thread_state64_t regs;
+		n = x86_THREAD_STATE64_COUNT;
+		if(me(thread_get_state(t->thread, x86_THREAD_STATE64,
+				(thread_state_t)&regs,
+				&n)) < 0)
+			return -1;
+		if(singlestep)
+			regs.rflags |= FLAGS_TF;
+		else
+			regs.rflags &= ~FLAGS_TF;
+		if(me(thread_set_state(t->thread, x86_THREAD_STATE64,
+				(thread_state_t)&regs,
+				x86_THREAD_STATE64_COUNT)) < 0)
+			return -1;
+	}
+
+	// Run.
+	n = sizeof info;
+	if(me(thread_info(t->thread, THREAD_BASIC_INFO, (thread_info_t)&info, &n)) < 0)
+		return -1;
+	for(i=0; i<info.suspend_count; i++)
+		if(me(thread_resume(t->thread)) < 0)
+			return -1;
+	return 0;
+}
+
+// Stop thread t.
+static int
+threadstop(Thread *t)
+{
+	if(threadstopped(t))
+		return 0;
+	if(me(thread_suspend(t->thread)) < 0)
+		return -1;
+	return 0;
+}
+
+// Callback for exc_server below.  Called when a thread we are
+// watching has an exception like hitting a breakpoint.
+kern_return_t
+catch_exception_raise(mach_port_t eport, mach_port_t thread,
+	mach_port_t task, exception_type_t exception,
+	exception_data_t code, mach_msg_type_number_t ncode)
+{
+	Thread *t;
+	int i;
+
+	t = nil;
+	for(i=0; i<nthr; i++){
+		if(thr[i].thread == thread){
+			t = &thr[i];
+			goto havet;
+		}
+	}
+	if(nthr > 0)
+		addpid(thr[0].pid, 1);
+	for(i=0; i<nthr; i++){
+		if(thr[i].thread == thread){
+			t = &thr[i];
+			goto havet;
+		}
+	}
+	fprint(2, "did not find thread in catch_exception_raise\n");
+	return KERN_SUCCESS;	// let thread continue
+
+havet:
+	t->exc = exception;
+	if(ncode > nelem(t->code))
+		ncode = nelem(t->code);
+	memmove(t->code, code, ncode*sizeof t->code[0]);
+
+	// Suspend thread, so that we can look at it & restart it later.
+	if(me(thread_suspend(thread)) < 0)
+		fprint(2, "catch_exception_raise thread_suspend: %r\n");
+
+	// Synchronize with waitstop below.
+	pthread_mutex_lock(&mu);
+	pthread_cond_broadcast(&cond);
+	pthread_mutex_unlock(&mu);
+
+	return KERN_SUCCESS;
+}
+
+// Exception watching thread, started in addpid above.
+static void*
+excthread(void *v)
+{
+	extern boolean_t exc_server();
+	mach_msg_server(exc_server, 2048, excport, 0);
+	return 0;
+}
+
+// Wait for pid to exit.
+static int exited;
+static void*
+waitthread(void *v)
+{
+	int pid, status;
+
+	pid = (int)(uintptr)v;
+	waitpid(pid, &status, 0);
+	exited = 1;
+	// Synchronize with waitstop below.
+	pthread_mutex_lock(&mu);
+	pthread_cond_broadcast(&cond);
+	pthread_mutex_unlock(&mu);
+	return nil;
+}
+
+// Wait for thread t to stop.
+static int
+waitstop(Thread *t)
+{
+	pthread_mutex_lock(&mu);
+	while(!exited && !threadstopped(t))
+		pthread_cond_wait(&cond, &mu);
+	pthread_mutex_unlock(&mu);
+	return 0;
+}
+
+int
+ctlproc(int id, char *msg)
+{
+	Thread *t;
+	int status;
+
+	// Hang/attached dance is for debugging newly exec'ed programs.
+	// After fork, the child does ctlproc("hang") before exec,
+	// and the parent does ctlproc("attached") and then waitstop.
+	// Using these requires the BSD ptrace interface, unlike everything
+	// else we do, which uses only the Mach interface.  Our goal here
+	// is to do as little as possible using ptrace and then flip over to Mach.
+
+	if(strcmp(msg, "hang") == 0)
+		return ptrace(PT_TRACE_ME, 0, 0, 0);
+
+	if(strcmp(msg, "attached") == 0){
+		// The pid "id" has done a ctlproc "hang" and then
+		// exec, so we should find it stoppped just before exec
+		// of the new program.
+		#undef waitpid
+		if(waitpid(id, &status, WUNTRACED) < 0){
+			fprint(2, "ctlproc attached waitpid: %r\n");
+			return -1;
+		}
+		if(WIFEXITED(status) || !WIFSTOPPED(status)){
+			fprint(2, "ctlproc attached: bad process state\n");
+			return -1;
+		}
+
+		// Find Mach thread for pid and suspend it.
+		t = addpid(id, 1);
+		if(t == nil)
+			return -1;
+		if(me(thread_suspend(t->thread)) < 0){
+			fprint(2, "ctlproc attached: thread_suspend: %r\n");
+			return -1;
+		}
+
+		// Let ptrace tell the process to keep going:
+		// then ptrace is out of the way and we're back in Mach land.
+		return ptrace(PT_CONTINUE, id, (caddr_t)1, 0);
+	}
+
+	// All the other control messages require a Thread structure.
+	if((t = idtotable(id)) == nil){
+		werrstr("no such thread");
+		return -1;
+	}
+
+	if(strcmp(msg, "kill") == 0)
+		return ptrace(PT_KILL, t->pid, 0, 0);
+
+	if(strcmp(msg, "start") == 0)
+		return threadstart(t, 0);
+
+	if(strcmp(msg, "stop") == 0)
+		return threadstop(t);
+
+	if(strcmp(msg, "startstop") == 0){
+		if(threadstart(t, 0) < 0)
+			return -1;
+		return waitstop(t);
+	}
+
+	if(strcmp(msg, "step") == 0){
+		if(threadstart(t, 1) < 0)
+			return -1;
+		return waitstop(t);
+	}
+
+	if(strcmp(msg, "waitstop") == 0)
+		return waitstop(t);
+
+	// sysstop not available on OS X
+
+	werrstr("unknown control message");
+	return -1;
+}
+
+char*
+procstatus(int id)
+{
+	Thread *t;
+
+	if((t = idtotable(id)) == nil)
+		return "gone!";
+
+	if(threadstopped(t))
+		return "Stopped";
+
+	return "Running";
+}
+
diff --git a/src/libmach/elf.h b/src/libmach/elf.h
new file mode 100644
index 0000000..8dbc983
--- /dev/null
+++ b/src/libmach/elf.h
@@ -0,0 +1,182 @@
+// Inferno libmach/elf.h
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/elf.h
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ *	Definitions needed for  accessing ELF headers.
+ *	32-bit and 64-bit structs differ.
+ */
+typedef struct {
+	uchar	ident[16];	/* ident bytes */
+	ushort	type;		/* file type */
+	ushort	machine;	/* target machine */
+	int	version;	/* file version */
+	uint32	elfentry;	/* start address */
+	uint32	phoff;		/* phdr file offset */
+	uint32	shoff;		/* shdr file offset */
+	int	flags;		/* file flags */
+	ushort	ehsize;		/* sizeof ehdr */
+	ushort	phentsize;	/* sizeof phdr */
+	ushort	phnum;		/* number phdrs */
+	ushort	shentsize;	/* sizeof shdr */
+	ushort	shnum;		/* number shdrs */
+	ushort	shstrndx;	/* shdr string index */
+} Ehdr32;
+
+typedef struct {
+	uchar	ident[16];	/* ident bytes */
+	ushort	type;		/* file type */
+	ushort	machine;	/* target machine */
+	int	version;	/* file version */
+	uvlong	elfentry;	/* start address */
+	uvlong	phoff;		/* phdr file offset */
+	uvlong	shoff;		/* shdr file offset */
+	int	flags;		/* file flags */
+	ushort	ehsize;		/* sizeof ehdr */
+	ushort	phentsize;	/* sizeof phdr */
+	ushort	phnum;		/* number phdrs */
+	ushort	shentsize;	/* sizeof shdr */
+	ushort	shnum;		/* number shdrs */
+	ushort	shstrndx;	/* shdr string index */
+} Ehdr64;
+
+typedef struct {
+	int	type;		/* entry type */
+	uint32	offset;		/* file offset */
+	uint32	vaddr;		/* virtual address */
+	uint32	paddr;		/* physical address */
+	int	filesz;		/* file size */
+	uint32	memsz;		/* memory size */
+	int	flags;		/* entry flags */
+	int	align;		/* memory/file alignment */
+} Phdr32;
+
+typedef struct {
+	int	type;		/* entry type */
+	int	flags;		/* entry flags */
+	uvlong	offset;		/* file offset */
+	uvlong	vaddr;		/* virtual address */
+	uvlong	paddr;		/* physical address */
+	uvlong	filesz;		/* file size */
+	uvlong	memsz;		/* memory size */
+	uvlong	align;		/* memory/file alignment */
+} Phdr64;
+
+typedef struct {
+	uint32	name;		/* section name */
+	uint32	type;		/* SHT_... */
+	uint32	flags;		/* SHF_... */
+	uint32	addr;		/* virtual address */
+	uint32	offset;		/* file offset */
+	uint32	size;		/* section size */
+	uint32	link;		/* misc info */
+	uint32	info;		/* misc info */
+	uint32	addralign;	/* memory alignment */
+	uint32	entsize;	/* entry size if table */
+} Shdr32;
+
+typedef struct {
+	uint32	name;		/* section name */
+	uint32	type;		/* SHT_... */
+	uvlong	flags;		/* SHF_... */
+	uvlong	addr;		/* virtual address */
+	uvlong	offset;		/* file offset */
+	uvlong	size;		/* section size */
+	uint32	link;		/* misc info */
+	uint32	info;		/* misc info */
+	uvlong	addralign;	/* memory alignment */
+	uvlong	entsize;	/* entry size if table */
+} Shdr64;
+
+enum {
+	/* Ehdr codes */
+	MAG0 = 0,		/* ident[] indexes */
+	MAG1 = 1,
+	MAG2 = 2,
+	MAG3 = 3,
+	CLASS = 4,
+	DATA = 5,
+	VERSION = 6,
+
+	ELFCLASSNONE = 0,	/* ident[CLASS] */
+	ELFCLASS32 = 1,
+	ELFCLASS64 = 2,
+	ELFCLASSNUM = 3,
+
+	ELFDATANONE = 0,	/* ident[DATA] */
+	ELFDATA2LSB = 1,
+	ELFDATA2MSB = 2,
+	ELFDATANUM = 3,
+
+	NOETYPE = 0,		/* type */
+	REL = 1,
+	EXEC = 2,
+	DYN = 3,
+	CORE = 4,
+
+	NONE = 0,		/* machine */
+	M32 = 1,		/* AT&T WE 32100 */
+	SPARC = 2,		/* Sun SPARC */
+	I386 = 3,		/* Intel 80386 */
+	M68K = 4,		/* Motorola 68000 */
+	M88K = 5,		/* Motorola 88000 */
+	I486 = 6,		/* Intel 80486 */
+	I860 = 7,		/* Intel i860 */
+	MIPS = 8,		/* Mips R2000 */
+	S370 = 9,		/* Amdhal	*/
+	SPARC64 = 18,		/* Sun SPARC v9 */
+	POWER = 20,		/* PowerPC */
+	ARM = 40,			/* ARM */
+	AMD64 = 62,		/* Amd64 */
+
+	NO_VERSION = 0,		/* version, ident[VERSION] */
+	CURRENT = 1,
+
+	/* Phdr Codes */
+	NOPTYPE = 0,		/* type */
+	LOAD = 1,
+	DYNAMIC = 2,
+	INTERP = 3,
+	NOTE = 4,
+	SHLIB = 5,
+	PHDR = 6,
+
+	R = 0x4,		/* flags */
+	W = 0x2,
+	X = 0x1,
+
+	/* Shdr Codes */
+	Progbits = 1,	/* section types */
+	Strtab = 3,
+	Nobits = 8,
+
+	Swrite = 1,	/* section attributes */
+	Salloc = 2,
+	Sexec = 4,
+};
+
+#define	ELF_MAG		((0x7f<<24) | ('E'<<16) | ('L'<<8) | 'F')
diff --git a/src/libmach/executable.c b/src/libmach/executable.c
new file mode 100644
index 0000000..0cc7d0f
--- /dev/null
+++ b/src/libmach/executable.c
@@ -0,0 +1,1274 @@
+// Inferno libmach/executable.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/executable.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include	<u.h>
+#include	<libc.h>
+#include	<bio.h>
+#include	<bootexec.h>
+#include	<mach.h>
+#include	"elf.h"
+#include	"macho.h"
+
+/*
+ *	All a.out header types.  The dummy entry allows canonical
+ *	processing of the union as a sequence of int32s
+ */
+
+typedef struct {
+	union{
+		/*struct { */
+			Exec exechdr;		/* a.out.h */
+		/*	uvlong hdr[1];*/
+		/*};*/
+		Ehdr32 elfhdr32;			/* elf.h */
+		Ehdr64 elfhdr64;			/* elf.h */
+		struct mipsexec mips;	/* bootexec.h */
+		struct mips4kexec mipsk4;	/* bootexec.h */
+		struct sparcexec sparc;	/* bootexec.h */
+		struct nextexec next;	/* bootexec.h */
+		Machhdr machhdr;	/* macho.h */
+	} e;
+	int32 dummy;			/* padding to ensure extra int32 */
+} ExecHdr;
+
+static	int	nextboot(int, Fhdr*, ExecHdr*);
+static	int	sparcboot(int, Fhdr*, ExecHdr*);
+static	int	mipsboot(int, Fhdr*, ExecHdr*);
+static	int	mips4kboot(int, Fhdr*, ExecHdr*);
+static	int	common(int, Fhdr*, ExecHdr*);
+static	int	commonllp64(int, Fhdr*, ExecHdr*);
+static	int	adotout(int, Fhdr*, ExecHdr*);
+static	int	elfdotout(int, Fhdr*, ExecHdr*);
+static	int	machdotout(int, Fhdr*, ExecHdr*);
+static	int	armdotout(int, Fhdr*, ExecHdr*);
+static	void	setsym(Fhdr*, int32, int32, int32, vlong);
+static	void	setdata(Fhdr*, uvlong, int32, vlong, int32);
+static	void	settext(Fhdr*, uvlong, uvlong, int32, vlong);
+static	void	hswal(void*, int, uint32(*)(uint32));
+static	uvlong	_round(uvlong, uint32);
+
+/*
+ *	definition of per-executable file type structures
+ */
+
+typedef struct Exectable{
+	int32	magic;			/* big-endian magic number of file */
+	char	*name;			/* executable identifier */
+	char	*dlmname;		/* dynamically loadable module identifier */
+	uchar	type;			/* Internal code */
+	uchar	_magic;			/* _MAGIC() magic */
+	Mach	*mach;			/* Per-machine data */
+	int32	hsize;			/* header size */
+	uint32	(*swal)(uint32);		/* beswal or leswal */
+	int	(*hparse)(int, Fhdr*, ExecHdr*);
+} ExecTable;
+
+extern	Mach	mmips;
+extern	Mach	mmips2le;
+extern	Mach	mmips2be;
+extern	Mach	msparc;
+extern	Mach	msparc64;
+extern	Mach	m68020;
+extern	Mach	mi386;
+extern	Mach	mamd64;
+extern	Mach	marm;
+extern	Mach	mpower;
+extern	Mach	mpower64;
+extern	Mach	malpha;
+
+/* BUG: FIX THESE WHEN NEEDED */
+Mach	mmips;
+Mach	mmips2le;
+Mach	mmips2be;
+Mach	msparc;
+Mach	msparc64;
+Mach	m68020;
+Mach	marm;
+Mach	mpower;
+Mach	mpower64;
+Mach	malpha;
+
+ExecTable exectab[] =
+{
+	{ V_MAGIC,			/* Mips v.out */
+		"mips plan 9 executable BE",
+		"mips plan 9 dlm BE",
+		FMIPS,
+		1,
+		&mmips,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ P_MAGIC,			/* Mips 0.out (r3k le) */
+		"mips plan 9 executable LE",
+		"mips plan 9 dlm LE",
+		FMIPSLE,
+		1,
+		&mmips,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ M_MAGIC,			/* Mips 4.out */
+		"mips 4k plan 9 executable BE",
+		"mips 4k plan 9 dlm BE",
+		FMIPS2BE,
+		1,
+		&mmips2be,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ N_MAGIC,			/* Mips 0.out */
+		"mips 4k plan 9 executable LE",
+		"mips 4k plan 9 dlm LE",
+		FMIPS2LE,
+		1,
+		&mmips2le,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ 0x160<<16,			/* Mips boot image */
+		"mips plan 9 boot image",
+		nil,
+		FMIPSB,
+		0,
+		&mmips,
+		sizeof(struct mipsexec),
+		beswal,
+		mipsboot },
+	{ (0x160<<16)|3,		/* Mips boot image */
+		"mips 4k plan 9 boot image",
+		nil,
+		FMIPSB,
+		0,
+		&mmips2be,
+		sizeof(struct mips4kexec),
+		beswal,
+		mips4kboot },
+	{ K_MAGIC,			/* Sparc k.out */
+		"sparc plan 9 executable",
+		"sparc plan 9 dlm",
+		FSPARC,
+		1,
+		&msparc,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ 0x01030107, 			/* Sparc boot image */
+		"sparc plan 9 boot image",
+		nil,
+		FSPARCB,
+		0,
+		&msparc,
+		sizeof(struct sparcexec),
+		beswal,
+		sparcboot },
+	{ U_MAGIC,			/* Sparc64 u.out */
+		"sparc64 plan 9 executable",
+		"sparc64 plan 9 dlm",
+		FSPARC64,
+		1,
+		&msparc64,
+		sizeof(Exec),
+		beswal,
+		adotout },
+	{ A_MAGIC,			/* 68020 2.out & boot image */
+		"68020 plan 9 executable",
+		"68020 plan 9 dlm",
+		F68020,
+		1,
+		&m68020,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ 0xFEEDFACE,			/* Next boot image */
+		"next plan 9 boot image",
+		nil,
+		FNEXTB,
+		0,
+		&m68020,
+		sizeof(struct nextexec),
+		beswal,
+		nextboot },
+	{ I_MAGIC,			/* I386 8.out & boot image */
+		"386 plan 9 executable",
+		"386 plan 9 dlm",
+		FI386,
+		1,
+		&mi386,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ S_MAGIC,			/* amd64 6.out & boot image */
+		"amd64 plan 9 executable",
+		"amd64 plan 9 dlm",
+		FAMD64,
+		1,
+		&mamd64,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ Q_MAGIC,			/* PowerPC q.out & boot image */
+		"power plan 9 executable",
+		"power plan 9 dlm",
+		FPOWER,
+		1,
+		&mpower,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ T_MAGIC,			/* power64 9.out & boot image */
+		"power64 plan 9 executable",
+		"power64 plan 9 dlm",
+		FPOWER64,
+		1,
+		&mpower64,
+		sizeof(Exec)+8,
+		nil,
+		commonllp64 },
+	{ ELF_MAG,			/* any elf32 or elf64 */
+		"elf executable",
+		nil,
+		FNONE,
+		0,
+		&mi386,
+		sizeof(Ehdr64),
+		nil,
+		elfdotout },
+	{ MACH64_MAG,			/* 64-bit MACH (apple mac) */
+		"mach executable",
+		nil,
+		FAMD64,
+		0,
+		&mamd64,
+		sizeof(Machhdr),
+		nil,
+		machdotout },
+	{ MACH32_MAG,			/* 32-bit MACH (apple mac) */
+		"mach executable",
+		nil,
+		FI386,
+		0,
+		&mi386,
+		sizeof(Machhdr),
+		nil,
+		machdotout },
+	{ E_MAGIC,			/* Arm 5.out and boot image */
+		"arm plan 9 executable",
+		"arm plan 9 dlm",
+		FARM,
+		1,
+		&marm,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ (143<<16)|0413,		/* (Free|Net)BSD Arm */
+		"arm *bsd executable",
+		nil,
+		FARM,
+		0,
+		&marm,
+		sizeof(Exec),
+		leswal,
+		armdotout },
+	{ L_MAGIC,			/* alpha 7.out */
+		"alpha plan 9 executable",
+		"alpha plan 9 dlm",
+		FALPHA,
+		1,
+		&malpha,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ 0x0700e0c3,			/* alpha boot image */
+		"alpha plan 9 boot image",
+		nil,
+		FALPHA,
+		0,
+		&malpha,
+		sizeof(Exec),
+		beswal,
+		common },
+	{ 0 },
+};
+
+Mach	*mach = &mi386;			/* Global current machine table */
+
+static ExecTable*
+couldbe4k(ExecTable *mp)
+{
+	Dir *d;
+	ExecTable *f;
+
+	if((d=dirstat("/proc/1/regs")) == nil)
+		return mp;
+	if(d->length < 32*8){		/* R3000 */
+		free(d);
+		return mp;
+	}
+	free(d);
+	for (f = exectab; f->magic; f++)
+		if(f->magic == M_MAGIC) {
+			f->name = "mips plan 9 executable on mips2 kernel";
+			return f;
+		}
+	return mp;
+}
+
+int
+crackhdr(int fd, Fhdr *fp)
+{
+	ExecTable *mp;
+	ExecHdr d;
+	int nb, ret;
+	uint32 magic;
+
+	fp->type = FNONE;
+	nb = read(fd, (char *)&d.e, sizeof(d.e));
+	if (nb <= 0)
+		return 0;
+
+	ret = 0;
+	magic = beswal(d.e.exechdr.magic);		/* big-endian */
+	for (mp = exectab; mp->magic; mp++) {
+		if (nb < mp->hsize)
+			continue;
+
+		/*
+		 * The magic number has morphed into something
+		 * with fields (the straw was DYN_MAGIC) so now
+		 * a flag is needed in Fhdr to distinguish _MAGIC()
+		 * magic numbers from foreign magic numbers.
+		 *
+		 * This code is creaking a bit and if it has to
+		 * be modified/extended much more it's probably
+		 * time to step back and redo it all.
+		 */
+		if(mp->_magic){
+			if(mp->magic != (magic & ~DYN_MAGIC))
+				continue;
+
+			if(mp->magic == V_MAGIC)
+				mp = couldbe4k(mp);
+
+			if ((magic & DYN_MAGIC) && mp->dlmname != nil)
+				fp->name = mp->dlmname;
+			else
+				fp->name = mp->name;
+		}
+		else{
+			if(mp->magic != magic)
+				continue;
+			fp->name = mp->name;
+		}
+		fp->type = mp->type;
+		fp->hdrsz = mp->hsize;		/* will be zero on bootables */
+		fp->_magic = mp->_magic;
+		fp->magic = magic;
+
+		mach = mp->mach;
+		if(mp->swal != nil)
+			hswal(&d, sizeof(d.e)/sizeof(uint32), mp->swal);
+		ret = mp->hparse(fd, fp, &d);
+		seek(fd, mp->hsize, 0);		/* seek to end of header */
+		break;
+	}
+	if(mp->magic == 0)
+		werrstr("unknown header type");
+	return ret;
+}
+
+/*
+ * Convert header to canonical form
+ */
+static void
+hswal(void *v, int n, uint32 (*swap)(uint32))
+{
+	uint32 *ulp;
+
+	for(ulp = v; n--; ulp++)
+		*ulp = (*swap)(*ulp);
+}
+
+/*
+ *	Crack a normal a.out-type header
+ */
+static int
+adotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	int32 pgsize;
+
+	USED(fd);
+	pgsize = mach->pgsize;
+	settext(fp, hp->e.exechdr.entry, pgsize+sizeof(Exec),
+			hp->e.exechdr.text, sizeof(Exec));
+	setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize),
+		hp->e.exechdr.data, fp->txtsz+sizeof(Exec), hp->e.exechdr.bss);
+	setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz);
+	return 1;
+}
+
+static void
+commonboot(Fhdr *fp)
+{
+	if (!(fp->entry & mach->ktmask))
+		return;
+
+	switch(fp->type) {				/* boot image */
+	case F68020:
+		fp->type = F68020B;
+		fp->name = "68020 plan 9 boot image";
+		break;
+	case FI386:
+		fp->type = FI386B;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "386 plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		break;
+	case FARM:
+		fp->type = FARMB;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "ARM plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		return;
+	case FALPHA:
+		fp->type = FALPHAB;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "alpha plan 9 boot image";
+		fp->dataddr = fp->txtaddr+fp->txtsz;
+		break;
+	case FPOWER:
+		fp->type = FPOWERB;
+		fp->txtaddr = (u32int)fp->entry;
+		fp->name = "power plan 9 boot image";
+		fp->dataddr = fp->txtaddr+fp->txtsz;
+		break;
+	case FAMD64:
+		fp->type = FAMD64B;
+		fp->txtaddr = fp->entry;
+		fp->name = "amd64 plan 9 boot image";
+		fp->dataddr = _round(fp->txtaddr+fp->txtsz, mach->pgsize);
+		break;
+	default:
+		return;
+	}
+	fp->hdrsz = 0;			/* header stripped */
+}
+
+/*
+ *	_MAGIC() style headers and
+ *	alpha plan9-style bootable images for axp "headerless" boot
+ *
+ */
+static int
+common(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	adotout(fd, fp, hp);
+	if(hp->e.exechdr.magic & DYN_MAGIC) {
+		fp->txtaddr = 0;
+		fp->dataddr = fp->txtsz;
+		return 1;
+	}
+	commonboot(fp);
+	return 1;
+}
+
+static int
+commonllp64(int unused, Fhdr *fp, ExecHdr *hp)
+{
+	int32 pgsize;
+	uvlong entry;
+
+	hswal(&hp->e, sizeof(Exec)/sizeof(int32), beswal);
+	if(!(hp->e.exechdr.magic & HDR_MAGIC))
+		return 0;
+
+	/*
+	 * There can be more magic here if the
+	 * header ever needs more expansion.
+	 * For now just catch use of any of the
+	 * unused bits.
+	 */
+	if((hp->e.exechdr.magic & ~DYN_MAGIC)>>16)
+		return 0;
+	entry = beswav(*(uvlong*)&hp->e.exechdr);
+
+	pgsize = mach->pgsize;
+	settext(fp, entry, pgsize+fp->hdrsz, hp->e.exechdr.text, fp->hdrsz);
+	setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize),
+		hp->e.exechdr.data, fp->txtsz+fp->hdrsz, hp->e.exechdr.bss);
+	setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz);
+
+	if(hp->e.exechdr.magic & DYN_MAGIC) {
+		fp->txtaddr = 0;
+		fp->dataddr = fp->txtsz;
+		return 1;
+	}
+	commonboot(fp);
+	return 1;
+}
+
+/*
+ *	mips bootable image.
+ */
+static int
+mipsboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+abort();
+#ifdef unused
+	USED(fd);
+	fp->type = FMIPSB;
+	switch(hp->e.exechdr.amagic) {
+	default:
+	case 0407:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
+			hp->e.tsize, sizeof(struct mipsexec)+4);
+		setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
+			fp->txtoff+hp->e.tsize, hp->e.bsize);
+		break;
+	case 0413:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.mentry, (u32int)hp->e.text_start,
+			hp->e.tsize, 0);
+		setdata(fp, (u32int)hp->e.data_start, hp->e.dsize,
+			hp->e.tsize, hp->e.bsize);
+		break;
+	}
+	setsym(fp, hp->e.nsyms, 0, hp->e.pcsize, hp->e.symptr);
+	fp->hdrsz = 0;			/* header stripped */
+#endif
+	return 1;
+}
+
+/*
+ *	mips4k bootable image.
+ */
+static int
+mips4kboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+abort();
+#ifdef unused
+	USED(fd);
+	fp->type = FMIPSB;
+	switch(hp->e.h.amagic) {
+	default:
+	case 0407:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
+			hp->e.h.tsize, sizeof(struct mips4kexec));
+		setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
+			fp->txtoff+hp->e.h.tsize, hp->e.h.bsize);
+		break;
+	case 0413:	/* some kind of mips */
+		settext(fp, (u32int)hp->e.h.mentry, (u32int)hp->e.h.text_start,
+			hp->e.h.tsize, 0);
+		setdata(fp, (u32int)hp->e.h.data_start, hp->e.h.dsize,
+			hp->e.h.tsize, hp->e.h.bsize);
+		break;
+	}
+	setsym(fp, hp->e.h.nsyms, 0, hp->e.h.pcsize, hp->e.h.symptr);
+	fp->hdrsz = 0;			/* header stripped */
+#endif
+	return 1;
+}
+
+/*
+ *	sparc bootable image
+ */
+static int
+sparcboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+abort();
+#ifdef unused
+	USED(fd);
+	fp->type = FSPARCB;
+	settext(fp, hp->e.sentry, hp->e.sentry, hp->e.stext,
+		sizeof(struct sparcexec));
+	setdata(fp, hp->e.sentry+hp->e.stext, hp->e.sdata,
+		fp->txtoff+hp->e.stext, hp->e.sbss);
+	setsym(fp, hp->e.ssyms, 0, hp->e.sdrsize, fp->datoff+hp->e.sdata);
+	fp->hdrsz = 0;			/* header stripped */
+#endif
+	return 1;
+}
+
+/*
+ *	next bootable image
+ */
+static int
+nextboot(int fd, Fhdr *fp, ExecHdr *hp)
+{
+abort();
+#ifdef unused
+	USED(fd);
+	fp->type = FNEXTB;
+	settext(fp, hp->e.textc.vmaddr, hp->e.textc.vmaddr,
+		hp->e.texts.size, hp->e.texts.offset);
+	setdata(fp, hp->e.datac.vmaddr, hp->e.datas.size,
+		hp->e.datas.offset, hp->e.bsss.size);
+	setsym(fp, hp->e.symc.nsyms, hp->e.symc.spoff, hp->e.symc.pcoff,
+		hp->e.symc.symoff);
+	fp->hdrsz = 0;			/* header stripped */
+#endif
+	return 1;
+}
+
+/*
+ * Elf32 and Elf64 binaries.
+ */
+static int
+elf64dotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+
+	uvlong (*swav)(uvlong);
+	uint32 (*swal)(uint32);
+	ushort (*swab)(ushort);
+	Ehdr64 *ep;
+	Phdr64 *ph;
+	Shdr64 *sh;
+	int i, it, id, is, phsz, shsz;
+
+	/* bitswap the header according to the DATA format */
+	ep = &hp->e.elfhdr64;
+	if(ep->ident[CLASS] != ELFCLASS64) {
+		werrstr("bad ELF class - not 32 bit or 64 bit");
+		return 0;
+	}
+	if(ep->ident[DATA] == ELFDATA2LSB) {
+		swab = leswab;
+		swal = leswal;
+		swav = leswav;
+	} else if(ep->ident[DATA] == ELFDATA2MSB) {
+		swab = beswab;
+		swal = beswal;
+		swav = beswav;
+	} else {
+		werrstr("bad ELF encoding - not big or little endian");
+		return 0;
+	}
+
+	ep->type = swab(ep->type);
+	ep->machine = swab(ep->machine);
+	ep->version = swal(ep->version);
+	ep->elfentry = swal(ep->elfentry);
+	ep->phoff = swav(ep->phoff);
+	ep->shoff = swav(ep->shoff);
+	ep->flags = swav(ep->flags);
+	ep->ehsize = swab(ep->ehsize);
+	ep->phentsize = swab(ep->phentsize);
+	ep->phnum = swab(ep->phnum);
+	ep->shentsize = swab(ep->shentsize);
+	ep->shnum = swab(ep->shnum);
+	ep->shstrndx = swab(ep->shstrndx);
+	if(ep->type != EXEC || ep->version != CURRENT)
+		return 0;
+
+	/* we could definitely support a lot more machines here */
+	fp->magic = ELF_MAG;
+	fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
+	switch(ep->machine) {
+	case AMD64:
+		mach = &mamd64;
+		fp->type = FAMD64;
+		break;
+	default:
+		return 0;
+	}
+
+	if(ep->phentsize != sizeof(Phdr64)) {
+		werrstr("bad ELF header size");
+		return 0;
+	}
+	phsz = sizeof(Phdr64)*ep->phnum;
+	ph = malloc(phsz);
+	if(!ph)
+		return 0;
+	seek(fd, ep->phoff, 0);
+	if(read(fd, ph, phsz) < 0) {
+		free(ph);
+		return 0;
+	}
+	hswal(ph, phsz/sizeof(uint32), swal);
+
+	shsz = sizeof(Shdr64)*ep->shnum;
+	sh = malloc(shsz);
+	if(sh) {
+		seek(fd, ep->shoff, 0);
+		if(read(fd, sh, shsz) < 0) {
+			free(sh);
+			sh = 0;
+		} else
+			hswal(sh, shsz/sizeof(uint32), swal);
+	}
+
+	/* find text, data and symbols and install them */
+	it = id = is = -1;
+	for(i = 0; i < ep->phnum; i++) {
+		if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
+			it = i;
+		else if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
+			id = i;
+		else if(ph[i].type == NOPTYPE && is == -1)
+			is = i;
+	}
+	if(it == -1 || id == -1) {
+		/*
+		 * The SPARC64 boot image is something of an ELF hack.
+		 * Text+Data+BSS are represented by ph[0].  Symbols
+		 * are represented by ph[1]:
+		 *
+		 *		filesz, memsz, vaddr, paddr, off
+		 * ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
+		 * ph[1] : symsz, lcsz, 0, 0, symoff
+		 */
+		if(ep->machine == SPARC64 && ep->phnum == 2) {
+			uint32 txtaddr, txtsz, dataddr, bsssz;
+
+			txtaddr = ph[0].vaddr | 0x80000000;
+			txtsz = ph[0].filesz - ph[0].paddr;
+			dataddr = txtaddr + txtsz;
+			bsssz = ph[0].memsz - ph[0].filesz;
+			settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
+			setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
+			setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset);
+			free(ph);
+			return 1;
+		}
+
+		werrstr("No TEXT or DATA sections");
+error:
+		free(ph);
+		free(sh);
+		return 0;
+	}
+
+	settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
+	setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
+	if(is != -1)
+		setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset);
+	else if(sh != 0){
+		char *buf;
+		uvlong symsize = 0;
+		uvlong symoff = 0;
+		uvlong pclnsz = 0;
+
+		/* load shstrtab names */
+		buf = malloc(sh[ep->shstrndx].size);
+		if (buf == 0)
+			goto done;
+		memset(buf, 0, sizeof buf);
+		seek(fd, sh[ep->shstrndx].offset, 0);
+		read(fd, buf, sh[ep->shstrndx].size);
+
+		for(i = 0; i < ep->shnum; i++) {
+			if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
+				symsize = sh[i].size;
+				symoff = sh[i].offset;
+			}
+			if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
+				if (sh[i].offset != symoff+symsize) {
+					werrstr("pc line table not contiguous with symbol table");
+					free(buf);
+					goto error;
+				}
+				pclnsz = sh[i].size;
+			}
+		}
+		setsym(fp, symsize, 0, pclnsz, symoff);
+		free(buf);
+	}
+done:
+	free(ph);
+	free(sh);
+	return 1;
+}
+
+static int
+elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+
+	uint32 (*swal)(uint32);
+	ushort (*swab)(ushort);
+	Ehdr32 *ep;
+	Phdr32 *ph;
+	int i, it, id, is, phsz, shsz;
+	Shdr32 *sh;
+
+	/* bitswap the header according to the DATA format */
+	ep = &hp->e.elfhdr32;
+	if(ep->ident[CLASS] != ELFCLASS32) {
+		return elf64dotout(fd, fp, hp);
+	}
+	if(ep->ident[DATA] == ELFDATA2LSB) {
+		swab = leswab;
+		swal = leswal;
+	} else if(ep->ident[DATA] == ELFDATA2MSB) {
+		swab = beswab;
+		swal = beswal;
+	} else {
+		werrstr("bad ELF encoding - not big or little endian");
+		return 0;
+	}
+
+	ep->type = swab(ep->type);
+	ep->machine = swab(ep->machine);
+	ep->version = swal(ep->version);
+	ep->elfentry = swal(ep->elfentry);
+	ep->phoff = swal(ep->phoff);
+	ep->shoff = swal(ep->shoff);
+	ep->flags = swal(ep->flags);
+	ep->ehsize = swab(ep->ehsize);
+	ep->phentsize = swab(ep->phentsize);
+	ep->phnum = swab(ep->phnum);
+	ep->shentsize = swab(ep->shentsize);
+	ep->shnum = swab(ep->shnum);
+	ep->shstrndx = swab(ep->shstrndx);
+	if(ep->type != EXEC || ep->version != CURRENT)
+		return 0;
+
+	/* we could definitely support a lot more machines here */
+	fp->magic = ELF_MAG;
+	fp->hdrsz = (ep->ehsize+ep->phnum*ep->phentsize+16)&~15;
+	switch(ep->machine) {
+	case I386:
+		mach = &mi386;
+		fp->type = FI386;
+		break;
+	case MIPS:
+		mach = &mmips;
+		fp->type = FMIPS;
+		break;
+	case SPARC64:
+		mach = &msparc64;
+		fp->type = FSPARC64;
+		break;
+	case POWER:
+		mach = &mpower;
+		fp->type = FPOWER;
+		break;
+	case ARM:
+		mach = &marm;
+		fp->type = FARM;
+		break;
+	default:
+		return 0;
+	}
+
+	if(ep->phentsize != sizeof(Phdr32)) {
+		werrstr("bad ELF header size");
+		return 0;
+	}
+	phsz = sizeof(Phdr32)*ep->phnum;
+	ph = malloc(phsz);
+	if(!ph)
+		return 0;
+	seek(fd, ep->phoff, 0);
+	if(read(fd, ph, phsz) < 0) {
+		free(ph);
+		return 0;
+	}
+	hswal(ph, phsz/sizeof(uint32), swal);
+
+	shsz = sizeof(Shdr32)*ep->shnum;
+	sh = malloc(shsz);
+	if(sh) {
+		seek(fd, ep->shoff, 0);
+		if(read(fd, sh, shsz) < 0) {
+			free(sh);
+			sh = 0;
+		} else
+			hswal(sh, shsz/sizeof(uint32), swal);
+	}
+
+	/* find text, data and symbols and install them */
+	it = id = is = -1;
+	for(i = 0; i < ep->phnum; i++) {
+		if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|X)) == (R|X) && it == -1)
+			it = i;
+		else if(ph[i].type == LOAD
+		&& (ph[i].flags & (R|W)) == (R|W) && id == -1)
+			id = i;
+		else if(ph[i].type == NOPTYPE && is == -1)
+			is = i;
+	}
+	if(it == -1 || id == -1) {
+		/*
+		 * The SPARC64 boot image is something of an ELF hack.
+		 * Text+Data+BSS are represented by ph[0].  Symbols
+		 * are represented by ph[1]:
+		 *
+		 *		filesz, memsz, vaddr, paddr, off
+		 * ph[0] : txtsz+datsz, txtsz+datsz+bsssz, txtaddr-KZERO, datasize, txtoff
+		 * ph[1] : symsz, lcsz, 0, 0, symoff
+		 */
+		if(ep->machine == SPARC64 && ep->phnum == 2) {
+			uint32 txtaddr, txtsz, dataddr, bsssz;
+
+			txtaddr = ph[0].vaddr | 0x80000000;
+			txtsz = ph[0].filesz - ph[0].paddr;
+			dataddr = txtaddr + txtsz;
+			bsssz = ph[0].memsz - ph[0].filesz;
+			settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, ph[0].offset);
+			setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
+			setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset);
+			free(ph);
+			return 1;
+		}
+
+		werrstr("No TEXT or DATA sections");
+error:
+		free(sh);
+		free(ph);
+		return 0;
+	}
+
+	settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
+	setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - ph[id].filesz);
+	if(is != -1)
+		setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset);
+	else if(sh != 0){
+		char *buf;
+		uvlong symsize = 0;
+		uvlong symoff = 0;
+		uvlong pclnsz = 0;
+
+		/* load shstrtab names */
+		buf = malloc(sh[ep->shstrndx].size);
+		if (buf == 0)
+			goto done;
+		memset(buf, 0, sizeof buf);
+		seek(fd, sh[ep->shstrndx].offset, 0);
+		read(fd, buf, sh[ep->shstrndx].size);
+
+		for(i = 0; i < ep->shnum; i++) {
+			if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
+				symsize = sh[i].size;
+				symoff = sh[i].offset;
+			}
+			if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
+				if (sh[i].offset != symoff+symsize) {
+					werrstr("pc line table not contiguous with symbol table");
+					free(buf);
+					goto error;
+				}
+				pclnsz = sh[i].size;
+			}
+		}
+		setsym(fp, symsize, 0, pclnsz, symoff);
+		free(buf);
+	}
+done:
+	free(sh);
+	free(ph);
+	return 1;
+}
+
+static int
+machdotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	uvlong (*swav)(uvlong);
+	uint32 (*swal)(uint32);
+	ushort (*swab)(ushort);
+	Machhdr *mp;
+	MachCmd **cmd;
+	MachSymSeg *symtab;
+	MachSymSeg *pclntab;
+	MachSeg64 *seg;
+	MachSect64 *sect;
+	MachSeg32 *seg32;
+	MachSect32 *sect32;
+	uvlong textsize, datasize, bsssize;
+	uchar *cmdbuf;
+	uchar *cmdp;
+	int i, hdrsize;
+	uint32 textva, textoff, datava, dataoff;
+
+	mp = &hp->e.machhdr;
+	if (leswal(mp->filetype) != MACH_EXECUTABLE_TYPE) {
+		werrstr("bad MACH executable type %#ux", leswal(mp->filetype));
+		return 0;
+	}
+
+	swab = leswab;
+	swal = leswal;
+	swav = leswav;
+
+	mp->magic = swal(mp->magic);
+	mp->cputype = swal(mp->cputype);
+	mp->cpusubtype = swal(mp->cpusubtype);
+	mp->filetype = swal(mp->filetype);
+	mp->ncmds = swal(mp->ncmds);
+	mp->sizeofcmds = swal(mp->sizeofcmds);
+	mp->flags = swal(mp->flags);
+	mp->reserved = swal(mp->reserved);
+	hdrsize = 0;
+
+	switch(mp->magic) {
+	case 0xFEEDFACE:	// 32-bit mach
+		if (mp->cputype != MACH_CPU_TYPE_X86) {
+			werrstr("bad MACH cpu type - not 386");
+			return 0;
+		}
+		if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86) {
+			werrstr("bad MACH cpu subtype - not 386");
+			return 0;
+		}
+		if (mp->filetype != MACH_EXECUTABLE_TYPE) {
+			werrstr("bad MACH executable type");
+			return 0;
+		}
+		mach = &mi386;
+		fp->type = FI386;
+		hdrsize = 28;
+		break;
+
+	case 0xFEEDFACF:	// 64-bit mach
+		if (mp->cputype != MACH_CPU_TYPE_X86_64) {
+			werrstr("bad MACH cpu type - not amd64");
+			return 0;
+		}
+
+		if (mp->cpusubtype != MACH_CPU_SUBTYPE_X86) {
+			werrstr("bad MACH cpu subtype - not amd64");
+			return 0;
+		}
+		mach = &mamd64;
+		fp->type = FAMD64;
+		hdrsize = 32;
+		break;
+
+	default:
+		werrstr("not mach %#ux", mp->magic);
+		return 0;
+	}
+
+	cmdbuf = malloc(mp->sizeofcmds);
+	seek(fd, hdrsize, 0);
+	if(read(fd, cmdbuf, mp->sizeofcmds) != mp->sizeofcmds) {
+		free(cmdbuf);
+		return 0;
+	}
+	cmd = malloc(mp->ncmds * sizeof(MachCmd*));
+	cmdp = cmdbuf;
+	textva = 0;
+	textoff = 0;
+	dataoff = 0;
+	datava = 0;
+	symtab = 0;
+	pclntab = 0;
+	textsize = datasize = bsssize = 0;
+	for (i = 0; i < mp->ncmds; i++) {
+		MachCmd *c;
+
+		cmd[i] = (MachCmd*)cmdp;
+		c = cmd[i];
+		c->type = swal(c->type);
+		c->size = swal(c->size);
+		switch(c->type) {
+		case MACH_SEGMENT_32:
+			if(mp->magic != 0xFEEDFACE) {
+				werrstr("segment 32 in mach 64");
+				goto bad;
+			}
+			seg32 = (MachSeg32*)c;
+			seg32->vmaddr = swav(seg32->vmaddr);
+			seg32->vmsize = swav(seg32->vmsize);
+			seg32->fileoff = swav(seg32->fileoff);
+			seg32->filesize = swav(seg32->filesize);
+			seg32->maxprot = swal(seg32->maxprot);
+			seg32->initprot = swal(seg32->initprot);
+			seg32->nsects = swal(seg32->nsects);
+			seg32->flags = swal(seg32->flags);
+			if (strcmp(seg32->segname, "__TEXT") == 0) {
+				textva = seg32->vmaddr;
+				textoff = seg32->fileoff;
+				sect32 = (MachSect32*)(cmdp + sizeof(MachSeg32));
+				if (strcmp(sect32->sectname, "__text") == 0) {
+					textsize = swal(sect32->size);
+				} else {
+					werrstr("no text section");
+					goto bad;
+				}
+			}
+			if (strcmp(seg32->segname, "__DATA") == 0) {
+				datava = seg32->vmaddr;
+				dataoff = seg32->fileoff;
+				sect32 = (MachSect32*)(cmdp + sizeof(MachSeg32));
+				if (strcmp(sect32->sectname, "__data") == 0) {
+					datasize = swal(sect32->size);
+				} else {
+					werrstr("no data section");
+					goto bad;
+				}
+				sect32++;
+				if (strcmp(sect32->sectname, "__bss") == 0) {
+					bsssize = swal(sect32->size);
+				} else {
+					werrstr("no bss section");
+					goto bad;
+				}
+			}
+			break;
+
+		case MACH_SEGMENT_64:
+			if(mp->magic != 0xFEEDFACF) {
+				werrstr("segment 32 in mach 64");
+				goto bad;
+			}
+			seg = (MachSeg64*)c;
+			seg->vmaddr = swav(seg->vmaddr);
+			seg->vmsize = swav(seg->vmsize);
+			seg->fileoff = swav(seg->fileoff);
+			seg->filesize = swav(seg->filesize);
+			seg->maxprot = swal(seg->maxprot);
+			seg->initprot = swal(seg->initprot);
+			seg->nsects = swal(seg->nsects);
+			seg->flags = swal(seg->flags);
+			if (strcmp(seg->segname, "__TEXT") == 0) {
+				textva = seg->vmaddr;
+				textoff = seg->fileoff;
+				sect = (MachSect64*)(cmdp + sizeof(MachSeg64));
+				if (strcmp(sect->sectname, "__text") == 0) {
+					textsize = swav(sect->size);
+				} else {
+					werrstr("no text section");
+					goto bad;
+				}
+			}
+			if (strcmp(seg->segname, "__DATA") == 0) {
+				datava = seg->vmaddr;
+				dataoff = seg->fileoff;
+				sect = (MachSect64*)(cmdp + sizeof(MachSeg64));
+				if (strcmp(sect->sectname, "__data") == 0) {
+					datasize = swav(sect->size);
+				} else {
+					werrstr("no data section");
+					goto bad;
+				}
+				sect++;
+				if (strcmp(sect->sectname, "__bss") == 0) {
+					bsssize = swav(sect->size);
+				} else {
+					werrstr("no bss section");
+					goto bad;
+				}
+			}
+			break;
+		case MACH_UNIXTHREAD:
+			break;
+		case MACH_SYMSEG:
+			if (symtab == 0)
+				symtab = (MachSymSeg*)c;
+			else if (pclntab == 0)
+				pclntab = (MachSymSeg*)c;
+			break;
+		}
+		cmdp += c->size;
+	}
+	if (textva == 0 || datava == 0) {
+		free(cmd);
+		free(cmdbuf);
+		return 0;
+	}
+	/* compute entry by taking address after header - weird - BUG? */
+	settext(fp, textva+sizeof(Machhdr) + mp->sizeofcmds, textva, textsize, textoff);
+	setdata(fp, datava, datasize, dataoff, bsssize);
+	if(symtab != 0)
+		setsym(fp, symtab->filesize, 0, pclntab? pclntab->filesize : 0, symtab->fileoff);
+	free(cmd);
+	free(cmdbuf);
+	return 1;
+bad:
+	free(cmd);
+	free(cmdbuf);
+	return 0;
+}
+
+/*
+ * (Free|Net)BSD ARM header.
+ */
+static int
+armdotout(int fd, Fhdr *fp, ExecHdr *hp)
+{
+	uvlong kbase;
+
+	USED(fd);
+	settext(fp, hp->e.exechdr.entry, sizeof(Exec), hp->e.exechdr.text, sizeof(Exec));
+	setdata(fp, fp->txtsz, hp->e.exechdr.data, fp->txtsz, hp->e.exechdr.bss);
+	setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, fp->datoff+fp->datsz);
+
+	kbase = 0xF0000000;
+	if ((fp->entry & kbase) == kbase) {		/* Boot image */
+		fp->txtaddr = kbase+sizeof(Exec);
+		fp->name = "ARM *BSD boot image";
+		fp->hdrsz = 0;		/* header stripped */
+		fp->dataddr = kbase+fp->txtsz;
+	}
+	return 1;
+}
+
+static void
+settext(Fhdr *fp, uvlong e, uvlong a, int32 s, vlong off)
+{
+	fp->txtaddr = a;
+	fp->entry = e;
+	fp->txtsz = s;
+	fp->txtoff = off;
+}
+
+static void
+setdata(Fhdr *fp, uvlong a, int32 s, vlong off, int32 bss)
+{
+	fp->dataddr = a;
+	fp->datsz = s;
+	fp->datoff = off;
+	fp->bsssz = bss;
+}
+
+static void
+setsym(Fhdr *fp, int32 symsz, int32 sppcsz, int32 lnpcsz, vlong symoff)
+{
+	fp->symsz = symsz;
+	fp->symoff = symoff;
+	fp->sppcsz = sppcsz;
+	fp->sppcoff = fp->symoff+fp->symsz;
+	fp->lnpcsz = lnpcsz;
+	fp->lnpcoff = fp->sppcoff+fp->sppcsz;
+}
+
+
+static uvlong
+_round(uvlong a, uint32 b)
+{
+	uvlong w;
+
+	w = (a/b)*b;
+	if (a!=w)
+		w += b;
+	return(w);
+}
diff --git a/src/libmach/fakeobj.c b/src/libmach/fakeobj.c
new file mode 100644
index 0000000..ea7ef01
--- /dev/null
+++ b/src/libmach/fakeobj.c
@@ -0,0 +1,29 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * obj.c
+ * routines universal to all object files
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ar.h>
+#include <mach.h>
+#include "obj.h"
+
+int _is2(char* x) { return 0; }
+int _is7(char* x) { return 0; }
+int _is9(char* x) { return 0; }
+int _isk(char* x) { return 0; }
+int _isq(char* x) { return 0; }
+int _isv(char* x) { return 0; }
+int _isu(char* x) { return 0; }
+int _read2(Biobuf* b, Prog* p) { return 0; }
+int _read7(Biobuf* b, Prog* p) { return 0; }
+int _read9(Biobuf* b, Prog* p) { return 0; }
+int _readk(Biobuf* b, Prog* p) { return 0; }
+int _readq(Biobuf* b, Prog* p) { return 0; }
+int _readv(Biobuf* b, Prog* p) { return 0; }
+int _readu(Biobuf* b, Prog* p) { return 0; }
diff --git a/src/libmach/linux.c b/src/libmach/linux.c
new file mode 100644
index 0000000..20e62ee
--- /dev/null
+++ b/src/libmach/linux.c
@@ -0,0 +1,970 @@
+// Derived from Plan 9 from User Space src/libmach/Linux.c
+// http://code.swtch.com/plan9port/src/tip/src/libmach/Linux.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.
+//	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+//	Portions Copyright © 1997-1999 Vita Nuova Limited.
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+//	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2001-2007 Russ Cox.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <u.h>
+#include <sys/syscall.h>	/* for tkill */
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/ptrace.h>
+#include <sys/signal.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#define Ureg Ureg32
+#include <ureg_x86.h>
+#undef Ureg
+#define Ureg Ureg64
+#include <ureg_amd64.h>
+#undef Ureg
+#undef waitpid
+
+// The old glibc used with crosstool compilers on thresher
+// doesn't know these numbers, but the Linux kernel
+// had them as far back as 2.6.0.
+#ifndef WSTOPPED
+#define WSTOPPED 2
+#define WCONTINUED 8
+#define WIFCONTINUED(x) ((x) == 0xffff)
+#endif
+#ifndef PTRACE_SETOPTIONS
+#define PTRACE_SETOPTIONS 0x4200
+#define PTRACE_GETEVENTMSG 0x4201
+#define PTRACE_O_TRACEFORK 0x2
+#define PTRACE_O_TRACEVFORK 0x4
+#define PTRACE_O_TRACECLONE 0x8
+#define PTRACE_O_TRACEEXEC 0x10
+#define PTRACE_O_TRACEVFORKDONE 0x20
+#define PTRACE_O_TRACEEXIT 0x40
+#define PTRACE_EVENT_FORK 0x1
+#define PTRACE_EVENT_VFORK 0x2
+#define PTRACE_EVENT_CLONE 0x3
+#define PTRACE_EVENT_EXEC 0x4
+#define PTRACE_EVENT_VFORK_DONE 0x5
+#define PTRACE_EVENT_EXIT 0x6
+#endif
+
+typedef struct Ureg64 Ureg64;
+
+static Maprw ptracesegrw;
+static Maprw ptraceregrw;
+
+// /usr/include/asm-x86_64/user.h
+struct user_regs_struct {
+	unsigned long r15,r14,r13,r12,rbp,rbx,r11,r10;
+	unsigned long r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax;
+	unsigned long rip,cs,eflags;
+	unsigned long rsp,ss;
+  	unsigned long fs_base, gs_base;
+	unsigned long ds,es,fs,gs;
+};
+
+// Linux gets very upset if a debugger forgets the reported state
+// of a debugged process, so we keep everything we know about
+// a debugged process in the LinuxThread structure.
+//
+// We can poll for state changes by calling waitpid and interpreting
+// the integer status code that comes back.  Wait1 does this.
+//
+// If the process is already running, it is an error to PTRACE_CONT it.
+//
+// If the process is already stopped, it is an error to stop it again.
+//
+// If the process is stopped because of a signal, the debugger must
+// relay the signal to the PTRACE_CONT call, or else the signal is
+// dropped.
+//
+// If the process exits, the debugger should detach so that the real
+// parent can reap the zombie.
+//
+// On first attach, the debugger should set a handful of flags in order
+// to catch future events like fork, clone, exec, etc.
+
+// One for every attached thread.
+typedef struct LinuxThread LinuxThread;
+struct LinuxThread
+{
+	int pid;
+	int tid;
+	int state;
+	int signal;
+	int child;
+	int exitcode;
+};
+
+static int trace = 0;
+
+static LinuxThread **thr;
+static int nthr;
+static int mthr;
+
+static int realpid(int pid);
+
+enum
+{
+	Unknown,
+	Detached,
+	Attached,
+	AttachStop,
+	Stopped,
+	Running,
+	Forking,
+	Vforking,
+	VforkDone,
+	Cloning,
+	Execing,
+	Exiting,
+	Exited,
+	Killed,
+
+	NSTATE,
+};
+
+static char* statestr[NSTATE] = {
+	"Unknown",
+	"Detached",
+	"Attached",
+	"AttachStop",
+	"Stopped",
+	"Running",
+	"Forking",
+	"Vforking",
+	"VforkDone",
+	"Cloning",
+	"Execing",
+	"Exiting",
+	"Exited",
+	"Killed"
+};
+
+static LinuxThread*
+attachthread(int pid, int tid, int *new, int newstate)
+{
+	int i, n, status;
+	LinuxThread **p, *t;
+	uintptr flags;
+
+	if(new)
+		*new = 0;
+
+	for(i=0; i<nthr; i++)
+		if((pid == 0 || thr[i]->pid == pid) && thr[i]->tid == tid) {
+			t = thr[i];
+			goto fixup;
+		}
+
+	if(!new)
+		return nil;
+
+	if(nthr >= mthr) {
+		n = mthr;
+		if(n == 0)
+			n = 64;
+		else
+			n *= 2;
+		p = realloc(thr, n*sizeof thr[0]);
+		if(p == nil)
+			return nil;
+		thr = p;
+		mthr = n;
+	}
+
+	t = malloc(sizeof *t);
+	if(t == nil)
+		return nil;
+
+	thr[nthr++] = t;
+	t->pid = pid;
+	t->tid = tid;
+	t->state = newstate;
+	if(trace)
+		fprint(2, "new thread %d %d\n", t->pid, t->tid);
+	if(new)
+		*new = 1;
+
+fixup:
+	if(t->state == Detached) {
+		if(ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) {
+			fprint(2, "ptrace ATTACH %d: %r\n", tid);
+			return nil;
+		}
+		t->state = Attached;
+	}
+
+	if(t->state == Attached) {
+		// wait for stop, so we can set options
+		if(waitpid(tid, &status, __WALL|WUNTRACED|WSTOPPED) < 0)
+			return nil;
+		if(!WIFSTOPPED(status)) {
+			fprint(2, "waitpid %d: status=%#x not stopped\n", tid);
+			return nil;
+		}
+		t->state = AttachStop;
+	}
+
+	if(t->state == AttachStop) {
+		// set options so we'll find out about new threads
+		flags = PTRACE_O_TRACEFORK |
+			PTRACE_O_TRACEVFORK |
+			PTRACE_O_TRACECLONE |
+			PTRACE_O_TRACEEXEC |
+			PTRACE_O_TRACEVFORKDONE |
+			PTRACE_O_TRACEEXIT;
+		if(ptrace(PTRACE_SETOPTIONS, tid, 0, (void*)flags) < 0)	{
+			fprint(2, "ptrace PTRACE_SETOPTIONS %d: %r\n", tid);
+			return nil;
+		}
+		t->state = Stopped;
+	}
+
+	return t;
+}
+
+static LinuxThread*
+findthread(int tid)
+{
+	return attachthread(0, tid, nil, 0);
+}
+
+int
+procthreadpids(int pid, int *p, int np)
+{
+	int i, n;
+	LinuxThread *t;
+
+	n = 0;
+	for(i=0; i<nthr; i++) {
+		t = thr[i];
+		if(t->pid == pid) {
+			switch(t->state) {
+			case Exited:
+			case Detached:
+			case Killed:
+				break;
+
+			default:
+				if(n < np)
+					p[n] = t->tid;
+				n++;
+				break;
+			}
+		}
+	}
+	return n;
+}
+
+// Execute a single wait and update the corresponding thread.
+static int
+wait1(int nohang)
+{
+	int tid, new, status, event;
+	ulong data;
+	LinuxThread *t;
+	enum
+	{
+		NormalStop = 0x137f
+	};
+
+	if(nohang != 0)
+		nohang = WNOHANG;
+
+	tid = waitpid(-1, &status, __WALL|WUNTRACED|WSTOPPED|WCONTINUED|nohang);
+	if(tid < 0)
+		return -1;
+	if(tid == 0)
+		return 0;
+
+	if(trace > 0 && status != NormalStop)
+		fprint(2, "TID %d: %#x\n", tid, status);
+
+	// If we've not heard of this tid, something is wrong.
+	t = findthread(tid);
+	if(t == nil) {
+		fprint(2, "ptrace waitpid: unexpected new tid %d status %#x\n", tid, status);
+		return -1;
+	}
+
+	if(WIFSTOPPED(status)) {
+		t->state = Stopped;
+		t->signal = WSTOPSIG(status);
+		if(trace)
+			fprint(2, "tid %d: stopped %#x%s\n", tid, status,
+				status != NormalStop ? " ***" : "");
+		if(t->signal == SIGTRAP && (event = status>>16) != 0) {	// ptrace event
+			switch(event) {
+			case PTRACE_EVENT_FORK:
+				t->state = Forking;
+				goto child;
+
+			case PTRACE_EVENT_VFORK:
+				t->state = Vforking;
+				goto child;
+
+			case PTRACE_EVENT_CLONE:
+				t->state = Cloning;
+				goto child;
+
+			child:
+				if(ptrace(PTRACE_GETEVENTMSG, t->tid, 0, &data) < 0) {
+					fprint(2, "ptrace GETEVENTMSG tid %d: %r\n", tid);
+					break;
+				}
+				t->child = data;
+				attachthread(t->pid, t->child, &new, Running);
+				if(!new)
+					fprint(2, "ptrace child: not new\n");
+				break;
+
+			case PTRACE_EVENT_EXEC:
+				t->state = Execing;
+				break;
+
+			case PTRACE_EVENT_VFORK_DONE:
+				t->state = VforkDone;
+				break;
+
+			case PTRACE_EVENT_EXIT:
+				if(trace)
+					fprint(2, "tid %d: exiting %#x\n", tid, status);
+				t->state = Exiting;
+				if(ptrace(PTRACE_GETEVENTMSG, t->tid, 0, &data) < 0) {
+					fprint(2, "ptrace GETEVENTMSG tid %d: %r\n", tid);
+					break;
+				}
+				t->exitcode = data;
+				break;
+			}
+		}
+	}
+	if(WIFCONTINUED(status)) {
+		if(trace)
+			fprint(2, "tid %d: continued %#x\n", tid, status);
+		t->state = Running;
+	}
+	if(WIFEXITED(status)) {
+		if(trace)
+			fprint(2, "tid %d: exited %#x\n", tid, status);
+		t->state = Exited;
+		t->exitcode = WEXITSTATUS(status);
+		t->signal = -1;
+		ptrace(PTRACE_DETACH, t->tid, 0, 0);
+		if(trace)
+			fprint(2, "tid %d: detach exited\n", tid);
+	}
+	if(WIFSIGNALED(status)) {
+		if(trace)
+			fprint(2, "tid %d: signaled %#x\n", tid, status);
+		t->state = Exited;
+		t->signal = WTERMSIG(status);
+		t->exitcode = -1;
+		ptrace(PTRACE_DETACH, t->tid, 0, 0);
+		if(trace)
+			fprint(2, "tid %d: detach signaled\n", tid);
+	}
+	return 1;
+}
+
+static int
+waitstop(LinuxThread *t)
+{
+	while(t->state == Running)
+		if(wait1(0) < 0)
+			return -1;
+	return 0;
+}
+
+// Attach to and stop all threads in process pid.
+// Must stop everyone in order to make sure we set
+// the "tell me about new threads" option in every
+// task.
+int
+attachallthreads(int pid)
+{
+	int tid, foundnew, new;
+	char buf[100];
+	DIR *d;
+	struct dirent *de;
+	LinuxThread *t;
+
+	if(pid == 0) {
+		fprint(2, "attachallthreads(0)\n");
+		return -1;
+	}
+
+	pid = realpid(pid);
+
+	snprint(buf, sizeof buf, "/proc/%d/task", pid);
+	if((d = opendir(buf)) == nil) {
+		fprint(2, "opendir %s: %r\n", buf);
+		return -1;
+	}
+
+	// Loop in case new threads are being created right now.
+	// We stop every thread as we find it, so eventually
+	// this has to stop (or the system runs out of procs).
+	do {
+		foundnew = 0;
+		while((de = readdir(d)) != nil) {
+			tid = atoi(de->d_name);
+			if(tid == 0)
+				continue;
+			t = attachthread(pid, tid, &new, Detached);
+			foundnew |= new;
+			if(t)
+				waitstop(t);
+		}
+		rewinddir(d);
+	} while(foundnew);
+	closedir(d);
+
+	return 0;
+}
+
+Map*
+attachproc(int pid, Fhdr *fp)
+{
+	Map *map;
+
+	if(pid == 0) {
+		fprint(2, "attachproc(0)\n");
+		return nil;
+	}
+
+	if(findthread(pid) == nil && attachallthreads(pid) < 0)
+		return nil;
+
+	map = newmap(0, 4);
+	if (!map)
+		return 0;
+	map->pid = pid;
+	if(mach->regsize)
+		setmap(map, -1, 0, mach->regsize, 0, "regs", ptraceregrw);
+//	if(mach->fpregsize)
+//		setmap(map, -1, mach->regsize, mach->regsize+mach->fpregsize, 0, "fpregs", ptraceregrw);
+	setmap(map, -1, fp->txtaddr, fp->txtaddr+fp->txtsz, fp->txtaddr, "*text", ptracesegrw);
+	setmap(map, -1, fp->dataddr, mach->utop, fp->dataddr, "*data", ptracesegrw);
+	return map;
+}
+
+void
+detachproc(Map *m)
+{
+	LinuxThread *t;
+
+	t = findthread(m->pid);
+	if(t != nil) {
+		ptrace(PTRACE_DETACH, t->tid, 0, 0);
+		t->state = Detached;
+		if(trace)
+			fprint(2, "tid %d: detachproc\n", t->tid);
+		// TODO(rsc): Reclaim thread structs somehow?
+	}
+	free(m);
+}
+
+/* /proc/pid/stat contains
+	pid
+	command in parens
+	0. state
+	1. ppid
+	2. pgrp
+	3. session
+	4. tty_nr
+	5. tpgid
+	6. flags (math=4, traced=10)
+	7. minflt
+	8. cminflt
+	9. majflt
+	10. cmajflt
+	11. utime
+	12. stime
+	13. cutime
+	14. cstime
+	15. priority
+	16. nice
+	17. 0
+	18. itrealvalue
+	19. starttime
+	20. vsize
+	21. rss
+	22. rlim
+	23. startcode
+	24. endcode
+	25. startstack
+	26. kstkesp
+	27. kstkeip
+	28. pending signal bitmap
+	29. blocked signal bitmap
+	30. ignored signal bitmap
+	31. caught signal bitmap
+	32. wchan
+	33. nswap
+	34. cnswap
+	35. exit_signal
+	36. processor
+*/
+
+static int
+readstat(int pid, char *buf, int nbuf, char **f, int nf)
+{
+	int fd, n;
+	char *p;
+
+	snprint(buf, nbuf, "/proc/%d/stat", pid);
+	if((fd = open(buf, OREAD)) < 0){
+		fprint(2, "open %s: %r\n", buf);
+		return -1;
+	}
+	n = read(fd, buf, nbuf-1);
+	close(fd);
+	if(n <= 0){
+		fprint(2, "read %s: %r\n", buf);
+		return -1;
+	}
+	buf[n] = 0;
+
+	/* command name is in parens, no parens afterward */
+	p = strrchr(buf, ')');
+	if(p == nil || *++p != ' '){
+		fprint(2, "bad format in /proc/%d/stat\n", pid);
+		return -1;
+	}
+	++p;
+
+	nf = tokenize(p, f, nf);
+	if(0) print("code 0x%lux-0x%lux stack 0x%lux kstk 0x%lux keip 0x%lux pending 0x%lux\n",
+		strtoul(f[23], 0, 0), strtoul(f[24], 0, 0), strtoul(f[25], 0, 0),
+		strtoul(f[26], 0, 0), strtoul(f[27], 0, 0), strtoul(f[28], 0, 0));
+
+	return nf;
+}
+
+static char*
+readstatus(int pid, char *buf, int nbuf, char *key)
+{
+	int fd, n;
+	char *p;
+
+	snprint(buf, nbuf, "/proc/%d/status", pid);
+	if((fd = open(buf, OREAD)) < 0){
+		fprint(2, "open %s: %r\n", buf);
+		return nil;
+	}
+	n = read(fd, buf, nbuf-1);
+	close(fd);
+	if(n <= 0){
+		fprint(2, "read %s: %r\n", buf);
+		return nil;
+	}
+	buf[n] = 0;
+	p = strstr(buf, key);
+	if(p)
+		return p+strlen(key);
+	return nil;
+}
+
+int
+procnotes(int pid, char ***pnotes)
+{
+	char buf[1024], *f[40];
+	int i, n, nf;
+	char *s, **notes;
+	ulong sigs;
+	extern char *_p9sigstr(int, char*);
+
+	*pnotes = nil;
+	nf = readstat(pid, buf, sizeof buf, f, nelem(f));
+	if(nf <= 28)
+		return -1;
+
+	sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
+	if(sigs == 0){
+		*pnotes = nil;
+		return 0;
+	}
+
+	notes = malloc(32*sizeof(char*));
+	if(notes == nil)
+		return -1;
+	memset(notes, 0, 32*sizeof(char*));
+	n = 0;
+	for(i=0; i<32; i++){
+		if((sigs&(1<<i)) == 0)
+			continue;
+		if((s = _p9sigstr(i, nil)) == nil)
+			continue;
+		notes[n++] = s;
+	}
+	*pnotes = notes;
+	return n;
+}
+
+static int
+realpid(int pid)
+{
+	char buf[1024], *p;
+
+	p = readstatus(pid, buf, sizeof buf, "\nTgid:");
+	if(p == nil)
+		return pid;
+	return atoi(p);
+}
+
+int
+ctlproc(int pid, char *msg)
+{
+	int new;
+	LinuxThread *t;
+	uintptr data;
+
+	while(wait1(1) > 0)
+		;
+
+	if(strcmp(msg, "attached") == 0){
+		t = attachthread(pid, pid, &new, Attached);
+		if(t == nil)
+			return -1;
+		return 0;
+	}
+
+	if(strcmp(msg, "hang") == 0){
+		if(pid == getpid())
+			return ptrace(PTRACE_TRACEME, 0, 0, 0);
+		werrstr("can only hang self");
+		return -1;
+	}
+
+	t = findthread(pid);
+	if(t == nil) {
+		werrstr("not attached to pid %d", pid);
+		return -1;
+	}
+	if(t->state == Exited) {
+		werrstr("pid %d has exited", pid);
+		return -1;
+	}
+	if(t->state == Killed) {
+		werrstr("pid %d has been killed", pid);
+		return -1;
+	}
+
+	if(strcmp(msg, "kill") == 0) {
+		if(ptrace(PTRACE_KILL, pid, 0, 0) < 0)
+			return -1;
+		t->state = Killed;
+		return 0;
+	}
+	if(strcmp(msg, "startstop") == 0){
+		if(ctlproc(pid, "start") < 0)
+			return -1;
+		return waitstop(t);
+	}
+	if(strcmp(msg, "sysstop") == 0){
+		if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
+			return -1;
+		t->state = Running;
+		return waitstop(t);
+	}
+	if(strcmp(msg, "stop") == 0){
+		if(trace > 1)
+			fprint(2, "tid %d: tkill stop\n", pid);
+		if(t->state == Stopped)
+			return 0;
+		if(syscall(__NR_tkill, pid, SIGSTOP) < 0)
+			return -1;
+		return waitstop(t);
+	}
+	if(strcmp(msg, "step") == 0){
+		if(t->state == Running) {
+			werrstr("cannot single-step unstopped %d", pid);
+			return -1;
+		}
+		if(ptrace(PTRACE_SINGLESTEP, pid, 0, 0) < 0)
+			return -1;
+		return waitstop(t);
+	}
+	if(strcmp(msg, "start") == 0) {
+		if(t->state == Running)
+			return 0;
+		data = 0;
+		if(t->state == Stopped && t->signal != SIGSTOP && t->signal != SIGTRAP)
+			data = t->signal;
+		if(trace && data)
+			fprint(2, "tid %d: continue %lud\n", pid, (ulong)data);
+		if(ptrace(PTRACE_CONT, pid, 0, (void*)data) < 0)
+			return -1;
+		t->state = Running;
+		return 0;
+	}
+	if(strcmp(msg, "waitstop") == 0) {
+		return waitstop(t);
+	}
+	werrstr("unknown control message '%s'", msg);
+	return -1;
+}
+
+char*
+proctextfile(int pid)
+{
+	static char buf[1024], pbuf[128];
+
+	snprint(pbuf, sizeof pbuf, "/proc/%d/exe", pid);
+	if(readlink(pbuf, buf, sizeof buf) >= 0)
+		return strdup(buf);
+	if(access(pbuf, AEXIST) >= 0)
+		return strdup(pbuf);
+	return nil;
+}
+
+
+static int
+ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n)
+{
+	int i;
+	uintptr u;
+	uchar buf[sizeof(uintptr)];
+
+	for(i=0; i<n; i+=sizeof(uintptr)){
+		if(isr){
+			errno = 0;
+			u = ptrace(type, pid, addr+i, 0);
+			if(errno)
+				goto ptraceerr;
+			if(n-i >= sizeof(uintptr))
+				*(uintptr*)((char*)v+i) = u;
+			else{
+				*(uintptr*)buf = u;
+				memmove((char*)v+i, buf, n-i);
+			}
+		}else{
+			if(n-i >= sizeof(uintptr))
+				u = *(uintptr*)((char*)v+i);
+			else{
+				errno = 0;
+				u = ptrace(xtype, pid, addr+i, 0);
+				if(errno)
+					return -1;
+				*(uintptr*)buf = u;
+				memmove(buf, (char*)v+i, n-i);
+				u = *(uintptr*)buf;
+			}
+			if(ptrace(type, pid, addr+i, u) < 0)
+				goto ptraceerr;
+		}
+	}
+	return 0;
+
+ptraceerr:
+	werrstr("ptrace %s addr=%#llux pid=%d: %r", isr ? "read" : "write", addr, pid);
+	return -1;
+}
+
+static int
+ptracesegrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+	return ptracerw(isr ? PTRACE_PEEKDATA : PTRACE_POKEDATA, PTRACE_PEEKDATA,
+		isr, map->pid, addr, v, n);
+}
+
+// If the debugger is compiled as an x86-64 program,
+// then all the ptrace register read/writes are done on
+// a 64-bit register set.  If the target program
+// is a 32-bit program, the debugger is expected to
+// read the bottom half of the relevant registers
+// out of the 64-bit set.
+
+// Linux 32-bit is
+//	BX CX DX SI DI BP AX DS ES FS GS OrigAX IP CS EFLAGS SP SS
+
+// Linux 64-bit is
+//	R15 R14 R13 R12 BP BX R11 R10 R9 R8 AX CX DX SI DI OrigAX IP CS EFLAGS SP SS FSBase GSBase DS ES FS GS
+
+// Go 32-bit is
+//	DI SI BP NSP BX DX CX AX GS FS ES DS TRAP ECODE PC CS EFLAGS SP SS
+
+// uint go32tolinux32tab[] = {
+//	4, 3, 5, 15, 0, 2, 1, 6, 10, 9, 8, 7, -1, -1, 12, 13, 14, 15, 16
+// };
+uint go32tolinux64tab[] = {
+	14, 13, 4, 19, 5, 12, 11, 10, 26, 25, 24, 23, -1, -1, 16, 17, 18, 19, 20
+};
+static int
+go32tolinux64(uvlong addr)
+{
+	int r;
+
+	if(addr%4 || addr/4 >= nelem(go32tolinux64tab))
+		return -1;
+	r = go32tolinux64tab[addr/4];
+	if(r < 0)
+		return -1;
+	return r*8;
+}
+
+extern Mach mi386;
+
+static int
+go2linux(uvlong addr)
+{
+	// TODO(rsc): If this file is being compiled in 32-bit mode,
+	// need to use the go32tolinux32 table instead.
+
+	if(mach == &mi386)
+		return go32tolinux64(addr);
+
+	switch(addr){
+	case offsetof(Ureg64, ax):
+		return offsetof(struct user_regs_struct, rax);
+	case offsetof(Ureg64, bx):
+		return offsetof(struct user_regs_struct, rbx);
+	case offsetof(Ureg64, cx):
+		return offsetof(struct user_regs_struct, rcx);
+	case offsetof(Ureg64, dx):
+		return offsetof(struct user_regs_struct, rdx);
+	case offsetof(Ureg64, si):
+		return offsetof(struct user_regs_struct, rsi);
+	case offsetof(Ureg64, di):
+		return offsetof(struct user_regs_struct, rdi);
+	case offsetof(Ureg64, bp):
+		return offsetof(struct user_regs_struct, rbp);
+	case offsetof(Ureg64, r8):
+		return offsetof(struct user_regs_struct, r8);
+	case offsetof(Ureg64, r9):
+		return offsetof(struct user_regs_struct, r9);
+	case offsetof(Ureg64, r10):
+		return offsetof(struct user_regs_struct, r10);
+	case offsetof(Ureg64, r11):
+		return offsetof(struct user_regs_struct, r11);
+	case offsetof(Ureg64, r12):
+		return offsetof(struct user_regs_struct, r12);
+	case offsetof(Ureg64, r13):
+		return offsetof(struct user_regs_struct, r13);
+	case offsetof(Ureg64, r14):
+		return offsetof(struct user_regs_struct, r14);
+	case offsetof(Ureg64, r15):
+		return offsetof(struct user_regs_struct, r15);
+	case offsetof(Ureg64, ds):
+		return offsetof(struct user_regs_struct, ds);
+	case offsetof(Ureg64, es):
+		return offsetof(struct user_regs_struct, es);
+	case offsetof(Ureg64, fs):
+		return offsetof(struct user_regs_struct, fs);
+	case offsetof(Ureg64, gs):
+		return offsetof(struct user_regs_struct, gs);
+	case offsetof(Ureg64, ip):
+		return offsetof(struct user_regs_struct, rip);
+	case offsetof(Ureg64, cs):
+		return offsetof(struct user_regs_struct, cs);
+	case offsetof(Ureg64, flags):
+		return offsetof(struct user_regs_struct, eflags);
+	case offsetof(Ureg64, sp):
+		return offsetof(struct user_regs_struct, rsp);
+	case offsetof(Ureg64, ss):
+		return offsetof(struct user_regs_struct, ss);
+	}
+	return -1;
+}
+
+static int
+ptraceregrw(Map *map, Seg *seg, uvlong addr, void *v, uint n, int isr)
+{
+	int laddr;
+	uvlong u;
+
+	if((laddr = go2linux(addr)) < 0){
+		if(isr){
+			memset(v, 0, n);
+			return 0;
+		}
+		werrstr("register %llud not available", addr);
+		return -1;
+	}
+
+	if(isr){
+		errno = 0;
+		u = ptrace(PTRACE_PEEKUSER, map->pid, laddr, 0);
+		if(errno)
+			goto ptraceerr;
+		switch(n){
+		case 1:
+			*(uint8*)v = u;
+			break;
+		case 2:
+			*(uint16*)v = u;
+			break;
+		case 4:
+			*(uint32*)v = u;
+			break;
+		case 8:
+			*(uint64*)v = u;
+			break;
+		default:
+			werrstr("bad register size");
+			return -1;
+		}
+	}else{
+		switch(n){
+		case 1:
+			u = *(uint8*)v;
+			break;
+		case 2:
+			u = *(uint16*)v;
+			break;
+		case 4:
+			u = *(uint32*)v;
+			break;
+		case 8:
+			u = *(uint64*)v;
+			break;
+		default:
+			werrstr("bad register size");
+			return -1;
+		}
+		if(ptrace(PTRACE_POKEUSER, map->pid, laddr, (void*)(uintptr)u) < 0)
+			goto ptraceerr;
+	}
+	return 0;
+
+ptraceerr:
+	werrstr("ptrace %s register laddr=%d pid=%d n=%d: %r", isr ? "read" : "write", laddr, map->pid, n);
+	return -1;
+}
+
+char*
+procstatus(int pid)
+{
+	LinuxThread *t;
+
+	t = findthread(pid);
+	if(t == nil)
+		return "???";
+
+	return statestr[t->state];
+}
diff --git a/src/libmach/machdata.c b/src/libmach/machdata.c
new file mode 100644
index 0000000..425a921
--- /dev/null
+++ b/src/libmach/machdata.c
@@ -0,0 +1,477 @@
+// Inferno libmach/machdata.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/machdata.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * Debugger utilities shared by at least two architectures
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+#define STARTSYM	"_main"
+#define PROFSYM		"_mainp"
+#define	FRAMENAME	".frame"
+
+extern	Machdata	mipsmach;
+
+int	asstype = AMIPS;		/* disassembler type */
+Machdata *machdata;		/* machine-dependent functions */
+
+int
+localaddr(Map *map, char *fn, char *var, uvlong *r, Rgetter rget)
+{
+	Symbol s;
+	uvlong fp, pc, sp, link;
+
+	if (!lookup(fn, 0, &s)) {
+		werrstr("function not found");
+		return -1;
+	}
+	pc = rget(map, mach->pc);
+	sp = rget(map, mach->sp);
+	if(mach->link)
+		link = rget(map, mach->link);
+	else
+		link = 0;
+	fp = machdata->findframe(map, s.value, pc, sp, link);
+	if (fp == 0) {
+		werrstr("stack frame not found");
+		return -1;
+	}
+
+	if (!var || !var[0]) {
+		*r = fp;
+		return 1;
+	}
+
+	if (findlocal(&s, var, &s) == 0) {
+		werrstr("local variable not found");
+		return -1;
+	}
+
+	switch (s.class) {
+	case CAUTO:
+		*r = fp - s.value;
+		break;
+	case CPARAM:		/* assume address size is stack width */
+		*r = fp + s.value + mach->szaddr;
+		break;
+	default:
+		werrstr("local variable not found: %d", s.class);
+		return -1;
+	}
+	return 1;
+}
+
+/*
+ * Print value v as s.name[+offset] if possible, or just v.
+ */
+int
+symoff(char *buf, int n, uvlong v, int space)
+{
+	Symbol s;
+	int r;
+	int32 delta;
+
+	r = delta = 0;		/* to shut compiler up */
+	if (v) {
+		r = findsym(v, space, &s);
+		if (r)
+			delta = v-s.value;
+		if (delta < 0)
+			delta = -delta;
+	}
+	if (v == 0 || r == 0)
+		return snprint(buf, n, "%llux", v);
+	if (s.type != 't' && s.type != 'T' && delta >= 4096)
+		return snprint(buf, n, "%llux", v);
+	else if (delta)
+		return snprint(buf, n, "%s+%#lux", s.name, delta);
+	else
+		return snprint(buf, n, "%s", s.name);
+}
+
+/*
+ *	Format floating point registers
+ *
+ *	Register codes in format field:
+ *	'X' - print as 32-bit hexadecimal value
+ *	'F' - 64-bit double register when modif == 'F'; else 32-bit single reg
+ *	'f' - 32-bit ieee float
+ *	'8' - big endian 80-bit ieee extended float
+ *	'3' - little endian 80-bit ieee extended float with hole in bytes 8&9
+ */
+int
+fpformat(Map *map, Reglist *rp, char *buf, int n, int modif)
+{
+	char reg[12];
+	uint32 r;
+
+	switch(rp->rformat)
+	{
+	case 'X':
+		if (get4(map, rp->roffs, &r) < 0)
+			return -1;
+		snprint(buf, n, "%lux", r);
+		break;
+	case 'F':	/* first reg of double reg pair */
+		if (modif == 'F')
+		if ((rp->rformat=='F') || (((rp+1)->rflags&RFLT) && (rp+1)->rformat == 'f')) {
+			if (get1(map, rp->roffs, (uchar *)reg, 8) < 0)
+				return -1;
+			machdata->dftos(buf, n, reg);
+			if (rp->rformat == 'F')
+				return 1;
+			return 2;
+		}
+			/* treat it like 'f' */
+		if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
+			return -1;
+		machdata->sftos(buf, n, reg);
+		break;
+	case 'f':	/* 32 bit float */
+		if (get1(map, rp->roffs, (uchar *)reg, 4) < 0)
+			return -1;
+		machdata->sftos(buf, n, reg);
+		break;
+	case '3':	/* little endian ieee 80 with hole in bytes 8&9 */
+		if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
+			return -1;
+		memmove(reg+10, reg+8, 2);	/* open hole */
+		memset(reg+8, 0, 2);		/* fill it */
+		leieee80ftos(buf, n, reg);
+		break;
+	case '8':	/* big-endian ieee 80 */
+		if (get1(map, rp->roffs, (uchar *)reg, 10) < 0)
+			return -1;
+		beieee80ftos(buf, n, reg);
+		break;
+	default:	/* unknown */
+		break;
+	}
+	return 1;
+}
+
+char *
+_hexify(char *buf, uint32 p, int zeros)
+{
+	uint32 d;
+
+	d = p/16;
+	if(d)
+		buf = _hexify(buf, d, zeros-1);
+	else
+		while(zeros--)
+			*buf++ = '0';
+	*buf++ = "0123456789abcdef"[p&0x0f];
+	return buf;
+}
+
+/*
+ * These routines assume that if the number is representable
+ * in IEEE floating point, it will be representable in the native
+ * double format.  Naive but workable, probably.
+ */
+int
+ieeedftos(char *buf, int n, uint32 h, uint32 l)
+{
+	double fr;
+	int exp;
+
+	if (n <= 0)
+		return 0;
+
+
+	if(h & (1L<<31)){
+		*buf++ = '-';
+		h &= ~(1L<<31);
+	}else
+		*buf++ = ' ';
+	n--;
+	if(l == 0 && h == 0)
+		return snprint(buf, n, "0.");
+	exp = (h>>20) & ((1L<<11)-1L);
+	if(exp == 0)
+		return snprint(buf, n, "DeN(%.8lux%.8lux)", h, l);
+	if(exp == ((1L<<11)-1L)){
+		if(l==0 && (h&((1L<<20)-1L)) == 0)
+			return snprint(buf, n, "Inf");
+		else
+			return snprint(buf, n, "NaN(%.8lux%.8lux)", h&((1L<<20)-1L), l);
+	}
+	exp -= (1L<<10) - 2L;
+	fr = l & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (l>>16) & ((1L<<16)-1L);
+	fr /= 1L<<16;
+	fr += (h & (1L<<20)-1L) | (1L<<20);
+	fr /= 1L<<21;
+	fr = ldexp(fr, exp);
+	return snprint(buf, n, "%.18g", fr);
+}
+
+int
+ieeesftos(char *buf, int n, uint32 h)
+{
+	double fr;
+	int exp;
+
+	if (n <= 0)
+		return 0;
+
+	if(h & (1L<<31)){
+		*buf++ = '-';
+		h &= ~(1L<<31);
+	}else
+		*buf++ = ' ';
+	n--;
+	if(h == 0)
+		return snprint(buf, n, "0.");
+	exp = (h>>23) & ((1L<<8)-1L);
+	if(exp == 0)
+		return snprint(buf, n, "DeN(%.8lux)", h);
+	if(exp == ((1L<<8)-1L)){
+		if((h&((1L<<23)-1L)) == 0)
+			return snprint(buf, n, "Inf");
+		else
+			return snprint(buf, n, "NaN(%.8lux)", h&((1L<<23)-1L));
+	}
+	exp -= (1L<<7) - 2L;
+	fr = (h & ((1L<<23)-1L)) | (1L<<23);
+	fr /= 1L<<24;
+	fr = ldexp(fr, exp);
+	return snprint(buf, n, "%.9g", fr);
+}
+
+int
+beieeesftos(char *buf, int n, void *s)
+{
+	return ieeesftos(buf, n, beswal(*(uint32*)s));
+}
+
+int
+beieeedftos(char *buf, int n, void *s)
+{
+	return ieeedftos(buf, n, beswal(*(uint32*)s), beswal(((uint32*)(s))[1]));
+}
+
+int
+leieeesftos(char *buf, int n, void *s)
+{
+	return ieeesftos(buf, n, leswal(*(uint32*)s));
+}
+
+int
+leieeedftos(char *buf, int n, void *s)
+{
+	return ieeedftos(buf, n, leswal(((uint32*)(s))[1]), leswal(*(uint32*)s));
+}
+
+/* packed in 12 bytes, with s[2]==s[3]==0; mantissa starts at s[4]*/
+int
+beieee80ftos(char *buf, int n, void *s)
+{
+	uchar *reg = (uchar*)s;
+	int i;
+	uint32 x;
+	uchar ieee[8+8];	/* room for slop */
+	uchar *p, *q;
+
+	memset(ieee, 0, sizeof(ieee));
+	/* sign */
+	if(reg[0] & 0x80)
+		ieee[0] |= 0x80;
+
+	/* exponent */
+	x = ((reg[0]&0x7F)<<8) | reg[1];
+	if(x == 0)		/* number is ±0 */
+		goto done;
+	if(x == 0x7FFF){
+		if(memcmp(reg+4, ieee+1, 8) == 0){ /* infinity */
+			x = 2047;
+		}else{				/* NaN */
+			x = 2047;
+			ieee[7] = 0x1;		/* make sure */
+		}
+		ieee[0] |= x>>4;
+		ieee[1] |= (x&0xF)<<4;
+		goto done;
+	}
+	x -= 0x3FFF;		/* exponent bias */
+	x += 1023;
+	if(x >= (1<<11) || ((reg[4]&0x80)==0 && x!=0))
+		return snprint(buf, n, "not in range");
+	ieee[0] |= x>>4;
+	ieee[1] |= (x&0xF)<<4;
+
+	/* mantissa */
+	p = reg+4;
+	q = ieee+1;
+	for(i=0; i<56; i+=8, p++, q++){	/* move one byte */
+		x = (p[0]&0x7F) << 1;
+		if(p[1] & 0x80)
+			x |= 1;
+		q[0] |= x>>4;
+		q[1] |= (x&0xF)<<4;
+	}
+    done:
+	return beieeedftos(buf, n, (void*)ieee);
+}
+
+int
+leieee80ftos(char *buf, int n, void *s)
+{
+	int i;
+	char *cp;
+	char b[12];
+
+	cp = (char*) s;
+	for(i=0; i<12; i++)
+		b[11-i] = *cp++;
+	return beieee80ftos(buf, n, b);
+}
+
+int
+cisctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
+{
+	Symbol s;
+	int found, i;
+	uvlong opc, moved;
+
+	USED(link);
+	i = 0;
+	opc = 0;
+	while(pc && opc != pc) {
+		moved = pc2sp(pc);
+		if (moved == ~0)
+			break;
+		found = findsym(pc, CTEXT, &s);
+		if (!found)
+			break;
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		sp += moved;
+		opc = pc;
+		if (geta(map, sp, &pc) < 0)
+			break;
+		(*trace)(map, pc, sp, &s);
+		sp += mach->szaddr;	/*assumes address size = stack width*/
+		if(++i > 40)
+			break;
+	}
+	return i;
+}
+
+int
+risctrace(Map *map, uvlong pc, uvlong sp, uvlong link, Tracer trace)
+{
+	int i;
+	Symbol s, f;
+	uvlong oldpc;
+
+	i = 0;
+	while(findsym(pc, CTEXT, &s)) {
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(pc == s.value)	/* at first instruction */
+			f.value = 0;
+		else if(findlocal(&s, FRAMENAME, &f) == 0)
+			break;
+
+		oldpc = pc;
+		if(s.type == 'L' || s.type == 'l' || pc <= s.value+mach->pcquant)
+			pc = link;
+		else
+			if (geta(map, sp, &pc) < 0)
+				break;
+
+		if(pc == 0 || (pc == oldpc && f.value == 0))
+			break;
+
+		sp += f.value;
+		(*trace)(map, pc-8, sp, &s);
+
+		if(++i > 40)
+			break;
+	}
+	return i;
+}
+
+uvlong
+ciscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
+{
+	Symbol s;
+	uvlong moved;
+
+	USED(link);
+	for(;;) {
+		moved = pc2sp(pc);
+		if (moved  == ~0)
+			break;
+		sp += moved;
+		findsym(pc, CTEXT, &s);
+		if (addr == s.value)
+			return sp;
+		if (geta(map, sp, &pc) < 0)
+			break;
+		sp += mach->szaddr;	/*assumes sizeof(addr) = stack width*/
+	}
+	return 0;
+}
+
+uvlong
+riscframe(Map *map, uvlong addr, uvlong pc, uvlong sp, uvlong link)
+{
+	Symbol s, f;
+
+	while (findsym(pc, CTEXT, &s)) {
+		if(strcmp(STARTSYM, s.name) == 0 || strcmp(PROFSYM, s.name) == 0)
+			break;
+
+		if(pc == s.value)	/* at first instruction */
+			f.value = 0;
+		else
+		if(findlocal(&s, FRAMENAME, &f) == 0)
+			break;
+
+		sp += f.value;
+		if (s.value == addr)
+			return sp;
+
+		if (s.type == 'L' || s.type == 'l' || pc-s.value <= mach->szaddr*2)
+			pc = link;
+		else
+		if (geta(map, sp-f.value, &pc) < 0)
+			break;
+	}
+	return 0;
+}
diff --git a/src/libmach/macho.h b/src/libmach/macho.h
new file mode 100644
index 0000000..df039d0
--- /dev/null
+++ b/src/libmach/macho.h
@@ -0,0 +1,99 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ *	Definitions needed for  accessing MACH object headers.
+ */
+
+typedef struct {
+	uint32	magic;		/* mach magic number identifier */
+	uint32	cputype;	/* cpu specifier */
+	uint32	cpusubtype;	/* machine specifier */
+	uint32	filetype;	/* type of file */
+	uint32	ncmds;		/* number of load commands */
+	uint32	sizeofcmds;	/* the size of all the load commands */
+	uint32	flags;		/* flags */
+	uint32	reserved;	/* reserved */
+} Machhdr;
+
+typedef struct {
+	uint32	type;	/* type of load command */
+	uint32	size;	/* total size in bytes */
+} MachCmd;
+
+typedef struct  {
+	MachCmd	cmd;
+	char		segname[16];	/* segment name */
+	uint32	vmaddr;		/* memory address of this segment */
+	uint32	vmsize;		/* memory size of this segment */
+	uint32	fileoff;	/* file offset of this segment */
+	uint32	filesize;	/* amount to map from the file */
+	uint32	maxprot;	/* maximum VM protection */
+	uint32	initprot;	/* initial VM protection */
+	uint32	nsects;		/* number of sections in segment */
+	uint32	flags;		/* flags */
+} MachSeg32; /* for 32-bit architectures */
+
+typedef struct  {
+	MachCmd	cmd;
+	char		segname[16];	/* segment name */
+	uvlong	vmaddr;		/* memory address of this segment */
+	uvlong	vmsize;		/* memory size of this segment */
+	uvlong	fileoff;	/* file offset of this segment */
+	uvlong	filesize;	/* amount to map from the file */
+	uint32	maxprot;	/* maximum VM protection */
+	uint32	initprot;	/* initial VM protection */
+	uint32	nsects;		/* number of sections in segment */
+	uint32	flags;		/* flags */
+} MachSeg64; /* for 64-bit architectures */
+
+typedef struct  {
+	MachCmd	cmd;
+	uint32	fileoff;	/* file offset of this segment */
+	uint32	filesize;	/* amount to map from the file */
+} MachSymSeg;
+
+typedef struct  {
+	char		sectname[16];	/* name of this section */
+	char		segname[16];	/* segment this section goes in */
+	uint32	addr;		/* memory address of this section */
+	uint32	size;		/* size in bytes of this section */
+	uint32	offset;		/* file offset of this section */
+	uint32	align;		/* section alignment (power of 2) */
+	uint32	reloff;		/* file offset of relocation entries */
+	uint32	nreloc;		/* number of relocation entries */
+	uint32	flags;		/* flags (section type and attributes)*/
+	uint32	reserved1;	/* reserved (for offset or index) */
+	uint32	reserved2;	/* reserved (for count or sizeof) */
+} MachSect32; /* for 32-bit architectures */
+
+typedef struct  {
+	char		sectname[16];	/* name of this section */
+	char		segname[16];	/* segment this section goes in */
+	uvlong	addr;		/* memory address of this section */
+	uvlong	size;		/* size in bytes of this section */
+	uint32	offset;		/* file offset of this section */
+	uint32	align;		/* section alignment (power of 2) */
+	uint32	reloff;		/* file offset of relocation entries */
+	uint32	nreloc;		/* number of relocation entries */
+	uint32	flags;		/* flags (section type and attributes)*/
+	uint32	reserved1;	/* reserved (for offset or index) */
+	uint32	reserved2;	/* reserved (for count or sizeof) */
+	uint32	reserved3;	/* reserved */
+} MachSect64; /* for 64-bit architectures */
+
+enum {
+	MACH_CPU_TYPE_X86_64 = (1<<24)|7,
+	MACH_CPU_TYPE_X86 = 7,
+	MACH_CPU_SUBTYPE_X86 = 3,
+	MACH_EXECUTABLE_TYPE = 2,
+	MACH_SEGMENT_32 = 1,	/* 32-bit mapped segment */
+	MACH_SEGMENT_64 = 0x19,	/* 64-bit mapped segment */
+	MACH_SYMSEG = 3,	/* obsolete gdb symtab, reused by go */
+	MACH_UNIXTHREAD = 0x5,	/* thread (for stack) */
+};
+
+
+#define	MACH64_MAG		((0xcf<<24) | (0xfa<<16) | (0xed<<8) | 0xfe)
+#define	MACH32_MAG		((0xce<<24) | (0xfa<<16) | (0xed<<8) | 0xfe)
diff --git a/src/libmach/map.c b/src/libmach/map.c
new file mode 100644
index 0000000..ebfe037
--- /dev/null
+++ b/src/libmach/map.c
@@ -0,0 +1,181 @@
+// Derived from Inferno libmach/map.c and
+// Plan 9 from User Space src/libmach/map.c
+//
+// http://code.swtch.com/plan9port/src/tip/src/libmach/map.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/map.c
+//
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.
+//	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+//	Portions Copyright © 1997-1999 Vita Nuova Limited.
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+//	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2001-2007 Russ Cox.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * file map routines
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+Map *
+newmap(Map *map, int n)
+{
+	int size;
+
+	size = sizeof(Map)+(n-1)*sizeof(Seg);
+	if (map == 0)
+		map = malloc(size);
+	else
+		map = realloc(map, size);
+	if (map == 0) {
+		werrstr("out of memory: %r");
+		return 0;
+	}
+	memset(map, 0, size);
+	map->nsegs = n;
+	return map;
+}
+
+int
+setmap(Map *map, int fd, uvlong b, uvlong e, vlong f, char *name, Maprw *rw)
+{
+	int i;
+
+	if (map == 0)
+		return 0;
+	for (i = 0; i < map->nsegs; i++)
+		if (!map->seg[i].inuse)
+			break;
+	if (i >= map->nsegs)
+		return 0;
+	map->seg[i].b = b;
+	map->seg[i].e = e;
+	map->seg[i].f = f;
+	map->seg[i].inuse = 1;
+	map->seg[i].name = name;
+	map->seg[i].fd = fd;
+	map->seg[i].rw = rw;
+	return 1;
+}
+
+/*
+static uvlong
+stacktop(int pid)
+{
+	char buf[64];
+	int fd;
+	int n;
+	char *cp;
+
+	snprint(buf, sizeof(buf), "/proc/%d/segment", pid);
+	fd = open(buf, 0);
+	if (fd < 0)
+		return 0;
+	n = read(fd, buf, sizeof(buf)-1);
+	close(fd);
+	buf[n] = 0;
+	if (strncmp(buf, "Stack", 5))
+		return 0;
+	for (cp = buf+5; *cp && *cp == ' '; cp++)
+		;
+	if (!*cp)
+		return 0;
+	cp = strchr(cp, ' ');
+	if (!cp)
+		return 0;
+	while (*cp && *cp == ' ')
+		cp++;
+	if (!*cp)
+		return 0;
+	return strtoull(cp, 0, 16);
+}
+*/
+
+int
+findseg(Map *map, char *name)
+{
+	int i;
+
+	if (!map)
+		return -1;
+	for (i = 0; i < map->nsegs; i++)
+		if (map->seg[i].inuse && !strcmp(map->seg[i].name, name))
+			return i;
+	return -1;
+}
+
+void
+unusemap(Map *map, int i)
+{
+	if (map != 0 && 0 <= i && i < map->nsegs)
+		map->seg[i].inuse = 0;
+}
+
+int
+fdrw(Map *map, Seg *s, uvlong addr, void *v, uint n, int isread)
+{
+	int tot, m;
+
+	for(tot=0; tot<n; tot+=m){
+		if(isread)
+			m = pread(s->fd, (uchar*)v+tot, n-tot, addr+tot);
+		else
+			m = pwrite(s->fd, (uchar*)v+tot, n-tot, addr+tot);
+		if(m == 0){
+			werrstr("short %s", isread ? "read" : "write");
+			return -1;
+		}
+		if(m < 0){
+			werrstr("%s %d at %#llux (+%#llux): %r", isread ? "read" : "write", n, addr, s->f);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+Map*
+loadmap(Map *map, int fd, Fhdr *fp)
+{
+	map = newmap(map, 2);
+	if (map == 0)
+		return 0;
+
+	map->seg[0].b = fp->txtaddr;
+	map->seg[0].e = fp->txtaddr+fp->txtsz;
+	map->seg[0].f = fp->txtoff;
+	map->seg[0].fd = fd;
+	map->seg[0].inuse = 1;
+	map->seg[0].name = "text";
+	map->seg[0].rw = fdrw;
+	map->seg[1].b = fp->dataddr;
+	map->seg[1].e = fp->dataddr+fp->datsz;
+	map->seg[1].f = fp->datoff;
+	map->seg[1].fd = fd;
+	map->seg[1].inuse = 1;
+	map->seg[1].name = "data";
+	map->seg[0].rw = fdrw;
+	return map;
+}
diff --git a/src/libmach/obj.c b/src/libmach/obj.c
new file mode 100644
index 0000000..aa914e2
--- /dev/null
+++ b/src/libmach/obj.c
@@ -0,0 +1,391 @@
+// Inferno libmach/obj.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/obj.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * obj.c
+ * routines universal to all object files
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ar.h>
+#include <mach.h>
+#include "obj.h"
+
+#define islocal(t)	((t)=='a' || (t)=='p')
+
+enum
+{
+	NNAMES	= 50,
+	MAXIS	= 8,		/* max length to determine if a file is a .? file */
+	MAXOFF	= 0x7fffffff,	/* larger than any possible local offset */
+	NHASH	= 1024,		/* must be power of two */
+	HASHMUL	= 79L,
+};
+
+int	_is2(char*),		/* in [$OS].c */
+	_is5(char*),
+	_is6(char*),
+	_is7(char*),
+	_is8(char*),
+	_is9(char*),
+	_isk(char*),
+	_isq(char*),
+	_isv(char*),
+	_isu(char*),
+	_read2(Biobuf*, Prog*),
+	_read5(Biobuf*, Prog*),
+	_read6(Biobuf*, Prog*),
+	_read7(Biobuf*, Prog*),
+	_read8(Biobuf*, Prog*),
+	_read9(Biobuf*, Prog*),
+	_readk(Biobuf*, Prog*),
+	_readq(Biobuf*, Prog*),
+	_readv(Biobuf*, Prog*),
+	_readu(Biobuf*, Prog*);
+
+typedef struct Obj	Obj;
+typedef struct Symtab	Symtab;
+
+struct	Obj		/* functions to handle each intermediate (.$O) file */
+{
+	char	*name;				/* name of each $O file */
+	int	(*is)(char*);			/* test for each type of $O file */
+	int	(*read)(Biobuf*, Prog*);	/* read for each type of $O file*/
+};
+
+static Obj	obj[] =
+{			/* functions to identify and parse each type of obj */
+	[Obj68020]	"68020 .2",	_is2, _read2,
+	[ObjAmd64]	"amd64 .6",	_is6, _read6,
+	[ObjArm]	"arm .5",	_is5, _read5,
+	[ObjAlpha]	"alpha .7",	_is7, _read7,
+	[Obj386]	"386 .8",	_is8, _read8,
+	[ObjSparc]	"sparc .k",	_isk, _readk,
+	[ObjPower]	"power .q",	_isq, _readq,
+	[ObjMips]	"mips .v",	_isv, _readv,
+	[ObjSparc64]	"sparc64 .u",	_isu, _readu,
+	[ObjPower64]	"power64 .9",	_is9, _read9,
+	[Maxobjtype]	0, 0
+};
+
+struct	Symtab
+{
+	struct	Sym 	s;
+	struct	Symtab	*next;
+};
+
+static	Symtab *hash[NHASH];
+static	Sym	*names[NNAMES];	/* working set of active names */
+
+static	int	processprog(Prog*,int);	/* decode each symbol reference */
+static	void	objreset(void);
+static	void	objlookup(int, char *, int, uint);
+static	void 	objupdate(int, int);
+
+static	int	sequence;
+
+int
+objtype(Biobuf *bp, char **name)
+{
+	int i;
+	char buf[MAXIS];
+	int c;
+
+Retry:
+	if(Bread(bp, buf, MAXIS) < MAXIS)
+		return -1;
+	Bseek(bp, -MAXIS, 1);
+	for (i = 0; i < Maxobjtype; i++) {
+		if (obj[i].is && (*obj[i].is)(buf)) {
+			if (name)
+				*name = obj[i].name;
+			return i;
+		}
+	}
+
+	/*
+	 * Maybe there's an import block we need to skip
+	 */
+	for(i = 0; i < MAXIS; i++) {
+		if(isalpha(buf[i]) || isdigit(buf[i]))
+			continue;
+		if(i == 0 || buf[i] != '\n')
+			return -1;
+		break;
+	}
+
+	/*
+	 * Found one.  Skip until "\n!\n"
+	 */
+	while((c = Bgetc(bp)) != Beof) {
+		if(c != '\n')
+			continue;
+		c = Bgetc(bp);
+		if(c != '!'){
+			Bungetc(bp);
+			continue;
+		}
+		c = Bgetc(bp);
+		if(c != '\n'){
+			Bungetc(bp);
+			continue;
+		}
+		goto Retry;
+	}
+	return -1;
+}
+
+int
+isar(Biobuf *bp)
+{
+	int n;
+	char magbuf[SARMAG];
+
+	n = Bread(bp, magbuf, SARMAG);
+	if(n == SARMAG && strncmp(magbuf, ARMAG, SARMAG) == 0)
+		return 1;
+	return 0;
+}
+
+/*
+ * determine what kind of object file this is and process it.
+ * return whether or not this was a recognized intermediate file.
+ */
+int
+readobj(Biobuf *bp, int objtype)
+{
+	Prog p;
+
+	if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
+		return 1;
+	objreset();
+	while ((*obj[objtype].read)(bp, &p))
+		if (!processprog(&p, 1))
+			return 0;
+	return 1;
+}
+
+int
+readar(Biobuf *bp, int objtype, vlong end, int doautos)
+{
+	Prog p;
+
+	if (objtype < 0 || objtype >= Maxobjtype || obj[objtype].is == 0)
+		return 1;
+	objreset();
+	while ((*obj[objtype].read)(bp, &p) && Boffset(bp) < end)
+		if (!processprog(&p, doautos))
+			return 0;
+	return 1;
+}
+
+/*
+ *	decode a symbol reference or definition
+ */
+static	int
+processprog(Prog *p, int doautos)
+{
+	if(p->kind == aNone)
+		return 1;
+	if(p->sym < 0 || p->sym >= NNAMES)
+		return 0;
+	switch(p->kind)
+	{
+	case aName:
+		if (!doautos)
+		if(p->type != 'U' && p->type != 'b')
+			break;
+		objlookup(p->sym, p->id, p->type, p->sig);
+		break;
+	case aText:
+		objupdate(p->sym, 'T');
+		break;
+	case aData:
+		objupdate(p->sym, 'D');
+		break;
+	default:
+		break;
+	}
+	return 1;
+}
+
+/*
+ * find the entry for s in the symbol array.
+ * make a new entry if it is not already there.
+ */
+static void
+objlookup(int id, char *name, int type, uint sig)
+{
+	int32 h;
+	char *cp;
+	Sym *s;
+	Symtab *sp;
+
+	s = names[id];
+	if(s && strcmp(s->name, name) == 0) {
+		s->type = type;
+		s->sig = sig;
+		return;
+	}
+
+	h = *name;
+	for(cp = name+1; *cp; h += *cp++)
+		h *= HASHMUL;
+	if(h < 0)
+		h = ~h;
+	h &= (NHASH-1);
+	if (type == 'U' || type == 'b' || islocal(type)) {
+		for(sp = hash[h]; sp; sp = sp->next)
+			if(strcmp(sp->s.name, name) == 0) {
+				switch(sp->s.type) {
+				case 'T':
+				case 'D':
+				case 'U':
+					if (type == 'U') {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				case 't':
+				case 'd':
+				case 'b':
+					if (type == 'b') {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				case 'a':
+				case 'p':
+					if (islocal(type)) {
+						names[id] = &sp->s;
+						return;
+					}
+					break;
+				default:
+					break;
+				}
+			}
+	}
+	sp = malloc(sizeof(Symtab));
+	sp->s.name = name;
+	sp->s.type = type;
+	sp->s.sig = sig;
+	sp->s.value = islocal(type) ? MAXOFF : 0;
+	sp->s.sequence = sequence++;
+	names[id] = &sp->s;
+	sp->next = hash[h];
+	hash[h] = sp;
+	return;
+}
+/*
+ *	traverse the symbol lists
+ */
+void
+objtraverse(void (*fn)(Sym*, void*), void *pointer)
+{
+	int i;
+	Symtab *s;
+
+	for(i = 0; i < NHASH; i++)
+		for(s = hash[i]; s; s = s->next)
+			(*fn)(&s->s, pointer);
+}
+
+/*
+ * update the offset information for a 'a' or 'p' symbol in an intermediate file
+ */
+void
+_offset(int id, vlong off)
+{
+	Sym *s;
+
+	s = names[id];
+	if (s && s->name[0] && islocal(s->type) && s->value > off)
+		s->value = off;
+}
+
+/*
+ * update the type of a global text or data symbol
+ */
+static void
+objupdate(int id, int type)
+{
+	Sym *s;
+
+	s = names[id];
+	if (s && s->name[0])
+		if (s->type == 'U')
+			s->type = type;
+		else if (s->type == 'b')
+			s->type = tolower(type);
+}
+
+/*
+ * look for the next file in an archive
+ */
+int
+nextar(Biobuf *bp, int offset, char *buf)
+{
+	struct ar_hdr a;
+	int i, r;
+	int32 arsize;
+
+	if (offset&01)
+		offset++;
+	Bseek(bp, offset, 0);
+	r = Bread(bp, &a, SAR_HDR);
+	if(r != SAR_HDR)
+		return 0;
+	if(strncmp(a.fmag, ARFMAG, sizeof(a.fmag)))
+		return -1;
+	for(i=0; i<sizeof(a.name) && i<SARNAME && a.name[i] != ' '; i++)
+		buf[i] = a.name[i];
+	buf[i] = 0;
+	arsize = strtol(a.size, 0, 0);
+	if (arsize&1)
+		arsize++;
+	return arsize + SAR_HDR;
+}
+
+static void
+objreset(void)
+{
+	int i;
+	Symtab *s, *n;
+
+	for(i = 0; i < NHASH; i++) {
+		for(s = hash[i]; s; s = n) {
+			n = s->next;
+			free(s->s.name);
+			free(s);
+		}
+		hash[i] = 0;
+	}
+	memset(names, 0, sizeof names);
+}
diff --git a/src/libmach/obj.h b/src/libmach/obj.h
new file mode 100644
index 0000000..35ec413
--- /dev/null
+++ b/src/libmach/obj.h
@@ -0,0 +1,53 @@
+// Inferno libmach/obj.h
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/obj.h
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+/*
+ * obj.h -- defs for dealing with object files
+ */
+
+typedef enum Kind		/* variable defs and references in obj */
+{
+	aNone,			/* we don't care about this prog */
+	aName,			/* introduces a name */
+	aText,			/* starts a function */
+	aData,			/* references to a global object */
+} Kind;
+
+typedef struct	Prog	Prog;
+
+struct Prog		/* info from .$O files */
+{
+	Kind	kind;		/* what kind of symbol */
+	char	type;		/* type of the symbol: ie, 'T', 'a', etc. */
+	char	sym;		/* index of symbol's name */
+	char	*id;		/* name for the symbol, if it introduces one */
+	uint	sig;		/* type signature for symbol */
+};
+
+#define UNKNOWN	'?'
+void		_offset(int, vlong);
diff --git a/src/libmach/setmach.c b/src/libmach/setmach.c
new file mode 100644
index 0000000..b887781
--- /dev/null
+++ b/src/libmach/setmach.c
@@ -0,0 +1,203 @@
+// Inferno libmach/setmach.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/setmach.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include	<u.h>
+#include	<libc.h>
+#include	<bio.h>
+#include	<mach.h>
+		/* table for selecting machine-dependent parameters */
+
+typedef	struct machtab Machtab;
+
+struct machtab
+{
+	char		*name;			/* machine name */
+	short		type;			/* executable type */
+	short		boottype;		/* bootable type */
+	int		asstype;		/* disassembler code */
+	Mach		*mach;			/* machine description */
+	Machdata	*machdata;		/* machine functions */
+};
+
+/*
+extern	Mach		mmips, msparc, m68020, mi386, mamd64,
+			marm, mmips2be, mmips2le, mpower, mpower64, malpha, msparc64;
+extern	Machdata	mipsmach, sparcmach, m68020mach, i386mach,
+			armmach, mipsmach2le, powermach, alphamach, sparc64mach;
+*/
+extern	Mach		mi386, mamd64;
+extern	Machdata		i386mach;
+
+/*
+ *	machine selection table.  machines with native disassemblers should
+ *	follow the plan 9 variant in the table; native modes are selectable
+ *	only by name.
+ */
+Machtab	machines[] =
+{
+	{	"386",				/*plan 9 386*/
+		FI386,
+		FI386B,
+		AI386,
+		&mi386,
+		&i386mach,	},
+	{	"amd64",			/*amd64*/
+		FAMD64,
+		FAMD64B,
+		AAMD64,
+		&mamd64,
+		&i386mach,	},
+#ifdef unused
+	{	"68020",			/*68020*/
+		F68020,
+		F68020B,
+		A68020,
+		&m68020,
+		&m68020mach,	},
+	{	"68020",			/*Next 68040 bootable*/
+		F68020,
+		FNEXTB,
+		A68020,
+		&m68020,
+		&m68020mach,	},
+	{	"mips2LE",			/*plan 9 mips2 little endian*/
+		FMIPS2LE,
+		0,
+		AMIPS,
+		&mmips2le,
+		&mipsmach2le, 	},
+	{	"mips",				/*plan 9 mips*/
+		FMIPS,
+		FMIPSB,
+		AMIPS,
+		&mmips,
+		&mipsmach, 	},
+	{	"mips2",			/*plan 9 mips2*/
+		FMIPS2BE,
+		FMIPSB,
+		AMIPS,
+		&mmips2be,
+		&mipsmach, 	},		/* shares debuggers with native mips */
+	{	"mipsco",			/*native mips - must follow plan 9*/
+		FMIPS,
+		FMIPSB,
+		AMIPSCO,
+		&mmips,
+		&mipsmach,	},
+	{	"sparc",			/*plan 9 sparc */
+		FSPARC,
+		FSPARCB,
+		ASPARC,
+		&msparc,
+		&sparcmach,	},
+	{	"sunsparc",			/*native sparc - must follow plan 9*/
+		FSPARC,
+		FSPARCB,
+		ASUNSPARC,
+		&msparc,
+		&sparcmach,	},
+	{	"86",				/*8086 - a peach of a machine*/
+		FI386,
+		FI386B,
+		AI8086,
+		&mi386,
+		&i386mach,	},
+	{	"arm",				/*ARM*/
+		FARM,
+		FARMB,
+		AARM,
+		&marm,
+		&armmach,	},
+	{	"power",			/*PowerPC*/
+		FPOWER,
+		FPOWERB,
+		APOWER,
+		&mpower,
+		&powermach,	},
+	{	"power64",			/*PowerPC*/
+		FPOWER64,
+		FPOWER64B,
+		APOWER64,
+		&mpower64,
+		&powermach,	},
+	{	"alpha",			/*Alpha*/
+		FALPHA,
+		FALPHAB,
+		AALPHA,
+		&malpha,
+		&alphamach,	},
+	{	"sparc64",			/*plan 9 sparc64 */
+		FSPARC64,
+		FSPARCB,			/* XXX? */
+		ASPARC64,
+		&msparc64,
+		&sparc64mach,	},
+#endif
+	{	0		},		/*the terminator*/
+};
+
+/*
+ *	select a machine by executable file type
+ */
+void
+machbytype(int type)
+{
+	Machtab *mp;
+
+	for (mp = machines; mp->name; mp++){
+		if (mp->type == type || mp->boottype == type) {
+			asstype = mp->asstype;
+			machdata = mp->machdata;
+			break;
+		}
+	}
+}
+/*
+ *	select a machine by name
+ */
+int
+machbyname(char *name)
+{
+	Machtab *mp;
+
+	if (!name) {
+		asstype = AAMD64;
+		machdata = &i386mach;
+		mach = &mamd64;
+		return 1;
+	}
+	for (mp = machines; mp->name; mp++){
+		if (strcmp(mp->name, name) == 0) {
+			asstype = mp->asstype;
+			machdata = mp->machdata;
+			mach = mp->mach;
+			return 1;
+		}
+	}
+	return 0;
+}
diff --git a/src/libmach/swap.c b/src/libmach/swap.c
new file mode 100644
index 0000000..bc296a3
--- /dev/null
+++ b/src/libmach/swap.c
@@ -0,0 +1,107 @@
+// Inferno libmach/swap.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/swap.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <u.h>
+
+/*
+ * big-endian short
+ */
+ushort
+beswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[0]<<8) | p[1];
+}
+
+/*
+ * big-endian int32
+ */
+uint32
+beswal(uint32 l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+ * big-endian vlong
+ */
+uvlong
+beswav(uvlong v)
+{
+	uchar *p;
+
+	p = (uchar*)&v;
+	return ((uvlong)p[0]<<56) | ((uvlong)p[1]<<48) | ((uvlong)p[2]<<40)
+				  | ((uvlong)p[3]<<32) | ((uvlong)p[4]<<24)
+				  | ((uvlong)p[5]<<16) | ((uvlong)p[6]<<8)
+				  | (uvlong)p[7];
+}
+
+/*
+ * little-endian short
+ */
+ushort
+leswab(ushort s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian int32
+ */
+uint32
+leswal(uint32 l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian vlong
+ */
+uvlong
+leswav(uvlong v)
+{
+	uchar *p;
+
+	p = (uchar*)&v;
+	return ((uvlong)p[7]<<56) | ((uvlong)p[6]<<48) | ((uvlong)p[5]<<40)
+				  | ((uvlong)p[4]<<32) | ((uvlong)p[3]<<24)
+				  | ((uvlong)p[2]<<16) | ((uvlong)p[1]<<8)
+				  | (uvlong)p[0];
+}
diff --git a/src/libmach/sym.c b/src/libmach/sym.c
new file mode 100644
index 0000000..997cc06
--- /dev/null
+++ b/src/libmach/sym.c
@@ -0,0 +1,1445 @@
+// Inferno libmach/sym.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/sym.c
+//
+// 	Copyright © 1994-1999 Lucent Technologies Inc.
+// 	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// 	Portions Copyright © 1997-1999 Vita Nuova Limited.
+// 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// 	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+#define	HUGEINT	0x7fffffff
+#define	NNAME	20		/* a relic of the past */
+
+typedef	struct txtsym Txtsym;
+typedef	struct file File;
+typedef	struct hist Hist;
+
+struct txtsym {				/* Text Symbol table */
+	int 	n;			/* number of local vars */
+	Sym	**locals;		/* array of ptrs to autos */
+	Sym	*sym;			/* function symbol entry */
+};
+
+struct hist {				/* Stack of include files & #line directives */
+	char	*name;			/* Assumes names Null terminated in file */
+	int32	line;			/* line # where it was included */
+	int32	offset;			/* line # of #line directive */
+};
+
+struct file {				/* Per input file header to history stack */
+	uvlong	addr;			/* address of first text sym */
+	union {
+		Txtsym	*txt;		/* first text symbol */
+		Sym	*sym;		/* only during initilization */
+	};
+	int	n;			/* size of history stack */
+	Hist	*hist;			/* history stack */
+};
+
+static	int	debug = 0;
+
+static	Sym	**autos;		/* Base of auto variables */
+static	File	*files;			/* Base of file arena */
+static	int	fmaxi;			/* largest file path index */
+static	Sym	**fnames;		/* file names path component table */
+static	Sym	**globals;		/* globals by addr table */
+static	Hist	*hist;			/* base of history stack */
+static	int	isbuilt;		/* internal table init flag */
+static	int32	nauto;			/* number of automatics */
+static	int32	nfiles;			/* number of files */
+static	int32	nglob;			/* number of globals */
+static	int32	nhist;			/* number of history stack entries */
+static	int32	nsym;			/* number of symbols */
+static	int	ntxt;			/* number of text symbols */
+static	uchar	*pcline;		/* start of pc-line state table */
+static	uchar 	*pclineend;		/* end of pc-line table */
+static	uchar	*spoff;			/* start of pc-sp state table */
+static	uchar	*spoffend;		/* end of pc-sp offset table */
+static	Sym	*symbols;		/* symbol table */
+static	Txtsym	*txt;			/* Base of text symbol table */
+static	uvlong	txtstart;		/* start of text segment */
+static	uvlong	txtend;			/* end of text segment */
+static	uvlong	firstinstr;		/* as found from symtab; needed for amd64 */
+
+static void	cleansyms(void);
+static int32	decodename(Biobuf*, Sym*);
+static short	*encfname(char*);
+static int 	fline(char*, int, int32, Hist*, Hist**);
+static void	fillsym(Sym*, Symbol*);
+static int	findglobal(char*, Symbol*);
+static int	findlocvar(Symbol*, char *, Symbol*);
+static int	findtext(char*, Symbol*);
+static int	hcomp(Hist*, short*);
+static int	hline(File*, short*, int32*);
+static void	printhist(char*, Hist*, int);
+static int	buildtbls(void);
+static int	symcomp(const void*, const void*);
+static int	symerrmsg(int, char*);
+static int	txtcomp(const void*, const void*);
+static int	filecomp(const void*, const void*);
+
+/*
+ *	initialize the symbol tables
+ */
+int
+syminit(int fd, Fhdr *fp)
+{
+	Sym *p;
+	int32 i, l, size;
+	vlong vl;
+	Biobuf b;
+	int svalsz;
+
+	if(fp->symsz == 0)
+		return 0;
+	if(fp->type == FNONE)
+		return 0;
+
+	cleansyms();
+	textseg(fp->txtaddr, fp);
+		/* minimum symbol record size = 4+1+2 bytes */
+	symbols = malloc((fp->symsz/(4+1+2)+1)*sizeof(Sym));
+	if(symbols == 0) {
+		werrstr("can't malloc %ld bytes", fp->symsz);
+		return -1;
+	}
+	Binit(&b, fd, OREAD);
+	Bseek(&b, fp->symoff, 0);
+	nsym = 0;
+	size = 0;
+	for(p = symbols; size < fp->symsz; p++, nsym++) {
+		if(fp->_magic && (fp->magic & HDR_MAGIC)){
+			svalsz = 8;
+			if(Bread(&b, &vl, 8) != 8)
+				return symerrmsg(8, "symbol");
+			p->value = beswav(vl);
+		}
+		else{
+			svalsz = 4;
+			if(Bread(&b, &l, 4) != 4)
+				return symerrmsg(4, "symbol");
+			p->value = (u32int)beswal(l);
+		}
+		if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type))
+			return symerrmsg(sizeof(p->value), "symbol");
+
+		i = decodename(&b, p);
+		if(i < 0)
+			return -1;
+		size += i+svalsz+sizeof(p->type);
+
+		if(svalsz == 8){
+			if(Bread(&b, &vl, 8) != 8)
+				return symerrmsg(8, "symbol");
+			p->gotype = beswav(vl);
+		}
+		else{
+			if(Bread(&b, &l, 4) != 4)
+				return symerrmsg(4, "symbol");
+			p->gotype = (u32int)beswal(l);
+		}
+		size += svalsz;
+
+		/* count global & auto vars, text symbols, and file names */
+		switch (p->type) {
+		case 'l':
+		case 'L':
+		case 't':
+		case 'T':
+			ntxt++;
+			break;
+		case 'd':
+		case 'D':
+		case 'b':
+		case 'B':
+			nglob++;
+			break;
+		case 'f':
+			if(strcmp(p->name, ".frame") == 0) {
+				p->type = 'm';
+				nauto++;
+			}
+			else if(p->value > fmaxi)
+				fmaxi = p->value;	/* highest path index */
+			break;
+		case 'a':
+		case 'p':
+		case 'm':
+			nauto++;
+			break;
+		case 'z':
+			if(p->value == 1) {		/* one extra per file */
+				nhist++;
+				nfiles++;
+			}
+			nhist++;
+			break;
+		default:
+			break;
+		}
+	}
+	if (debug)
+		print("NG: %ld NT: %d NF: %d\n", nglob, ntxt, fmaxi);
+	if (fp->sppcsz) {			/* pc-sp offset table */
+		spoff = (uchar *)malloc(fp->sppcsz);
+		if(spoff == 0) {
+			werrstr("can't malloc %ld bytes", fp->sppcsz);
+			return -1;
+		}
+		Bseek(&b, fp->sppcoff, 0);
+		if(Bread(&b, spoff, fp->sppcsz) != fp->sppcsz){
+			spoff = 0;
+			return symerrmsg(fp->sppcsz, "sp-pc");
+		}
+		spoffend = spoff+fp->sppcsz;
+	}
+	if (fp->lnpcsz) {			/* pc-line number table */
+		pcline = (uchar *)malloc(fp->lnpcsz);
+		if(pcline == 0) {
+			werrstr("can't malloc %ld bytes", fp->lnpcsz);
+			return -1;
+		}
+		Bseek(&b, fp->lnpcoff, 0);
+		if(Bread(&b, pcline, fp->lnpcsz) != fp->lnpcsz){
+			pcline = 0;
+			return symerrmsg(fp->lnpcsz, "pc-line");
+		}
+		pclineend = pcline+fp->lnpcsz;
+	}
+	return nsym;
+}
+
+static int
+symerrmsg(int n, char *table)
+{
+	werrstr("can't read %d bytes of %s table", n, table);
+	return -1;
+}
+
+static int32
+decodename(Biobuf *bp, Sym *p)
+{
+	char *cp;
+	int c1, c2;
+	int32 n;
+	vlong o;
+
+	if((p->type & 0x80) == 0) {		/* old-style, fixed length names */
+		p->name = malloc(NNAME);
+		if(p->name == 0) {
+			werrstr("can't malloc %d bytes", NNAME);
+			return -1;
+		}
+		if(Bread(bp, p->name, NNAME) != NNAME)
+			return symerrmsg(NNAME, "symbol");
+		Bseek(bp, 3, 1);
+		return NNAME+3;
+	}
+
+	p->type &= ~0x80;
+	if(p->type == 'z' || p->type == 'Z') {
+		o = Bseek(bp, 0, 1);
+		if(Bgetc(bp) < 0) {
+			werrstr("can't read symbol name");
+			return -1;
+		}
+		for(;;) {
+			c1 = Bgetc(bp);
+			c2 = Bgetc(bp);
+			if(c1 < 0 || c2 < 0) {
+				werrstr("can't read symbol name");
+				return -1;
+			}
+			if(c1 == 0 && c2 == 0)
+				break;
+		}
+		n = Bseek(bp, 0, 1)-o;
+		p->name = malloc(n);
+		if(p->name == 0) {
+			werrstr("can't malloc %ld bytes", n);
+			return -1;
+		}
+		Bseek(bp, -n, 1);
+		if(Bread(bp, p->name, n) != n) {
+			werrstr("can't read %ld bytes of symbol name", n);
+			return -1;
+		}
+	} else {
+		cp = Brdline(bp, '\0');
+		if(cp == 0) {
+			werrstr("can't read symbol name");
+			return -1;
+		}
+		n = Blinelen(bp);
+		p->name = malloc(n);
+		if(p->name == 0) {
+			werrstr("can't malloc %ld bytes", n);
+			return -1;
+		}
+		strcpy(p->name, cp);
+	}
+	return n;
+}
+
+/*
+ *	free any previously loaded symbol tables
+ */
+static void
+cleansyms(void)
+{
+	if(globals)
+		free(globals);
+	globals = 0;
+	nglob = 0;
+	if(txt)
+		free(txt);
+	txt = 0;
+	ntxt = 0;
+	if(fnames)
+		free(fnames);
+	fnames = 0;
+	fmaxi = 0;
+
+	if(files)
+		free(files);
+	files = 0;
+	nfiles = 0;
+	if(hist)
+		free(hist);
+	hist = 0;
+	nhist = 0;
+	if(autos)
+		free(autos);
+	autos = 0;
+	nauto = 0;
+	isbuilt = 0;
+	if(symbols)
+		free(symbols);
+	symbols = 0;
+	nsym = 0;
+	if(spoff)
+		free(spoff);
+	spoff = 0;
+	if(pcline)
+		free(pcline);
+	pcline = 0;
+}
+
+/*
+ *	delimit the text segment
+ */
+void
+textseg(uvlong base, Fhdr *fp)
+{
+	txtstart = base;
+	txtend = base+fp->txtsz;
+}
+
+/*
+ *	symbase: return base and size of raw symbol table
+ *		(special hack for high access rate operations)
+ */
+Sym *
+symbase(int32 *n)
+{
+	*n = nsym;
+	return symbols;
+}
+
+/*
+ *	Get the ith symbol table entry
+ */
+Sym *
+getsym(int index)
+{
+	if(index >= 0 && index < nsym)
+		return &symbols[index];
+	return 0;
+}
+
+/*
+ *	initialize internal symbol tables
+ */
+static int
+buildtbls(void)
+{
+	int32 i;
+	int j, nh, ng, nt;
+	File *f;
+	Txtsym *tp;
+	Hist *hp;
+	Sym *p, **ap;
+
+	if(isbuilt)
+		return 1;
+	isbuilt = 1;
+			/* allocate the tables */
+	firstinstr = 0;
+	if(nglob) {
+		globals = malloc(nglob*sizeof(*globals));
+		if(!globals) {
+			werrstr("can't malloc global symbol table");
+			return 0;
+		}
+	}
+	if(ntxt) {
+		txt = malloc(ntxt*sizeof(*txt));
+		if (!txt) {
+			werrstr("can't malloc text symbol table");
+			return 0;
+		}
+	}
+	fnames = malloc((fmaxi+1)*sizeof(*fnames));
+	if (!fnames) {
+		werrstr("can't malloc file name table");
+		return 0;
+	}
+	memset(fnames, 0, (fmaxi+1)*sizeof(*fnames));
+	files = malloc(nfiles*sizeof(*files));
+	if(!files) {
+		werrstr("can't malloc file table");
+		return 0;
+	}
+	hist = malloc(nhist*sizeof(Hist));
+	if(hist == 0) {
+		werrstr("can't malloc history stack");
+		return 0;
+	}
+	autos = malloc(nauto*sizeof(Sym*));
+	if(autos == 0) {
+		werrstr("can't malloc auto symbol table");
+		return 0;
+	}
+		/* load the tables */
+	ng = nt = nh = 0;
+	f = 0;
+	tp = 0;
+	i = nsym;
+	hp = hist;
+	ap = autos;
+	for(p = symbols; i-- > 0; p++) {
+//print("sym %d type %c name %s value %llux\n", p-symbols, p->type, p->name, p->value);
+		switch(p->type) {
+		case 'D':
+		case 'd':
+		case 'B':
+		case 'b':
+			if(debug)
+				print("Global: %s %llux\n", p->name, p->value);
+			globals[ng++] = p;
+			break;
+		case 'z':
+			if(p->value == 1) {		/* New file */
+				if(f) {
+					f->n = nh;
+					f->hist[nh].name = 0;	/* one extra */
+					hp += nh+1;
+					f++;
+				}
+				else
+					f = files;
+				f->hist = hp;
+				f->sym = 0;
+				f->addr = 0;
+				nh = 0;
+			}
+				/* alloc one slot extra as terminator */
+			f->hist[nh].name = p->name;
+			f->hist[nh].line = p->value;
+			f->hist[nh].offset = 0;
+			if(debug)
+				printhist("-> ", &f->hist[nh], 1);
+			nh++;
+			break;
+		case 'Z':
+			if(f && nh > 0)
+				f->hist[nh-1].offset = p->value;
+			break;
+		case 'T':
+		case 't':	/* Text: terminate history if first in file */
+		case 'L':
+		case 'l':
+			tp = &txt[nt++];
+			tp->n = 0;
+			tp->sym = p;
+			tp->locals = ap;
+			if(debug)
+				print("TEXT: %s at %llux\n", p->name, p->value);
+			if (firstinstr == 0 || p->value < firstinstr)
+				firstinstr = p->value;
+			if(f && !f->sym) {			/* first  */
+				f->sym = p;
+				f->addr = p->value;
+			}
+			break;
+		case 'a':
+		case 'p':
+		case 'm':		/* Local Vars */
+			if(!tp)
+				print("Warning: Free floating local var: %s\n",
+					p->name);
+			else {
+				if(debug)
+					print("Local: %s %llux\n", p->name, p->value);
+				tp->locals[tp->n] = p;
+				tp->n++;
+				ap++;
+			}
+			break;
+		case 'f':		/* File names */
+			if(debug)
+				print("Fname: %s\n", p->name);
+			fnames[p->value] = p;
+			break;
+		default:
+			break;
+		}
+	}
+		/* sort global and text tables into ascending address order */
+	qsort(globals, nglob, sizeof(Sym*), symcomp);
+	qsort(txt, ntxt, sizeof(Txtsym), txtcomp);
+	qsort(files, nfiles, sizeof(File), filecomp);
+	tp = txt;
+	for(i = 0, f = files; i < nfiles; i++, f++) {
+		for(j = 0; j < ntxt; j++) {
+			if(f->sym == tp->sym) {
+				if(debug) {
+					print("LINK: %s to at %llux", f->sym->name, f->addr);
+					printhist("... ", f->hist, 1);
+				}
+				f->txt = tp++;
+				break;
+			}
+			if(++tp >= txt+ntxt)	/* wrap around */
+				tp = txt;
+		}
+	}
+	return 1;
+}
+
+/*
+ * find symbol function.var by name.
+ *	fn != 0 && var != 0	=> look for fn in text, var in data
+ *	fn != 0 && var == 0	=> look for fn in text
+ *	fn == 0 && var != 0	=> look for var first in text then in data space.
+ */
+int
+lookup(char *fn, char *var, Symbol *s)
+{
+	int found;
+
+	if(buildtbls() == 0)
+		return 0;
+	if(fn) {
+		found = findtext(fn, s);
+		if(var == 0)		/* case 2: fn not in text */
+			return found;
+		else if(!found)		/* case 1: fn not found */
+			return 0;
+	} else if(var) {
+		found = findtext(var, s);
+		if(found)
+			return 1;	/* case 3: var found in text */
+	} else return 0;		/* case 4: fn & var == zero */
+
+	if(found)
+		return findlocal(s, var, s);	/* case 1: fn found */
+	return findglobal(var, s);		/* case 3: var not found */
+
+}
+
+/*
+ * strcmp, but allow '_' to match center dot (rune 00b7 == bytes c2 b7)
+ */
+int
+cdotstrcmp(char *sym, char *user) {
+	for (;;) {
+		while (*sym == *user) {
+			if (*sym++ == '\0')
+				return 0;
+			user++;
+		}
+		/* unequal - but maybe '_' matches center dot */
+		if (user[0] == '_' && (sym[0]&0xFF) == 0xc2 && (sym[1]&0xFF) == 0xb7) {
+			/* '_' matches center dot - advance and continue */
+			user++;
+			sym += 2;
+			continue;
+		}
+		break;
+	}
+	return *user - *sym;
+}
+
+/*
+ * find a function by name
+ */
+static int
+findtext(char *name, Symbol *s)
+{
+	int i;
+
+	for(i = 0; i < ntxt; i++) {
+		if(cdotstrcmp(txt[i].sym->name, name) == 0) {
+			fillsym(txt[i].sym, s);
+			s->handle = (void *) &txt[i];
+			s->index = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+/*
+ * find global variable by name
+ */
+static int
+findglobal(char *name, Symbol *s)
+{
+	int32 i;
+
+	for(i = 0; i < nglob; i++) {
+		if(cdotstrcmp(globals[i]->name, name) == 0) {
+			fillsym(globals[i], s);
+			s->index = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	find the local variable by name within a given function
+ */
+int
+findlocal(Symbol *s1, char *name, Symbol *s2)
+{
+	if(s1 == 0)
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+	return findlocvar(s1, name, s2);
+}
+
+/*
+ *	find the local variable by name within a given function
+ *		(internal function - does no parameter validation)
+ */
+static int
+findlocvar(Symbol *s1, char *name, Symbol *s2)
+{
+	Txtsym *tp;
+	int i;
+
+	tp = (Txtsym *)s1->handle;
+	if(tp && tp->locals) {
+		for(i = 0; i < tp->n; i++)
+			if (cdotstrcmp(tp->locals[i]->name, name) == 0) {
+				fillsym(tp->locals[i], s2);
+				s2->handle = (void *)tp;
+				s2->index = tp->n-1 - i;
+				return 1;
+			}
+	}
+	return 0;
+}
+
+/*
+ *	Get ith text symbol
+ */
+int
+textsym(Symbol *s, int index)
+{
+
+	if(buildtbls() == 0)
+		return 0;
+	if(index < 0 || index >= ntxt)
+		return 0;
+	fillsym(txt[index].sym, s);
+	s->handle = (void *)&txt[index];
+	s->index = index;
+	return 1;
+}
+
+/*
+ *	Get ith file name
+ */
+int
+filesym(int index, char *buf, int n)
+{
+	Hist *hp;
+
+	if(buildtbls() == 0)
+		return 0;
+	if(index < 0 || index >= nfiles)
+		return 0;
+	hp = files[index].hist;
+	if(!hp || !hp->name)
+		return 0;
+	return fileelem(fnames, (uchar*)hp->name, buf, n);
+}
+
+/*
+ *	Lookup name of local variable located at an offset into the frame.
+ *	The type selects either a parameter or automatic.
+ */
+int
+getauto(Symbol *s1, int off, int type, Symbol *s2)
+{
+	Txtsym *tp;
+	Sym *p;
+	int i, t;
+
+	if(s1 == 0)
+		return 0;
+	if(type == CPARAM)
+		t = 'p';
+	else if(type == CAUTO)
+		t = 'a';
+	else
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+	tp = (Txtsym *)s1->handle;
+	if(tp == 0)
+		return 0;
+	for(i = 0; i < tp->n; i++) {
+		p = tp->locals[i];
+		if(p->type == t && p->value == off) {
+			fillsym(p, s2);
+			s2->handle = s1->handle;
+			s2->index = tp->n-1 - i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Find text symbol containing addr; binary search assumes text array is sorted by addr
+ */
+static int
+srchtext(uvlong addr)
+{
+	uvlong val;
+	int top, bot, mid;
+	Sym *sp;
+
+	val = addr;
+	bot = 0;
+	top = ntxt;
+	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+		sp = txt[mid].sym;
+		if(val < sp->value)
+			top = mid;
+		else if(mid != ntxt-1 && val >= txt[mid+1].sym->value)
+			bot = mid;
+		else
+			return mid;
+	}
+	return -1;
+}
+
+/*
+ * Find data symbol containing addr; binary search assumes data array is sorted by addr
+ */
+static int
+srchdata(uvlong addr)
+{
+	uvlong val;
+	int top, bot, mid;
+	Sym *sp;
+
+	bot = 0;
+	top = nglob;
+	val = addr;
+	for(mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+		sp = globals[mid];
+		if(val < sp->value)
+			top = mid;
+		else if(mid < nglob-1 && val >= globals[mid+1]->value)
+			bot = mid;
+		else
+			return mid;
+	}
+	return -1;
+}
+
+/*
+ * Find symbol containing val in specified search space
+ * There is a special case when a value falls beyond the end
+ * of the text segment; if the search space is CTEXT, that value
+ * (usually etext) is returned.  If the search space is CANY, symbols in the
+ * data space are searched for a match.
+ */
+int
+findsym(uvlong val, int type, Symbol *s)
+{
+	int i;
+
+	if(buildtbls() == 0)
+		return 0;
+
+	if(type == CTEXT || type == CANY) {
+		i = srchtext(val);
+		if(i >= 0) {
+			if(type == CTEXT || i != ntxt-1) {
+				fillsym(txt[i].sym, s);
+				s->handle = (void *) &txt[i];
+				s->index = i;
+				return 1;
+			}
+		}
+	}
+	if(type == CDATA || type == CANY) {
+		i = srchdata(val);
+		if(i >= 0) {
+			fillsym(globals[i], s);
+			s->index = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	Find the start and end address of the function containing addr
+ */
+int
+fnbound(uvlong addr, uvlong *bounds)
+{
+	int i;
+
+	if(buildtbls() == 0)
+		return 0;
+
+	i = srchtext(addr);
+	if(0 <= i && i < ntxt-1) {
+		bounds[0] = txt[i].sym->value;
+		bounds[1] = txt[i+1].sym->value;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * get the ith local symbol for a function
+ * the input symbol table is reverse ordered, so we reverse
+ * accesses here to maintain approx. parameter ordering in a stack trace.
+ */
+int
+localsym(Symbol *s, int index)
+{
+	Txtsym *tp;
+
+	if(s == 0 || index < 0)
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+
+	tp = (Txtsym *)s->handle;
+	if(tp && tp->locals && index < tp->n) {
+		fillsym(tp->locals[tp->n-index-1], s);	/* reverse */
+		s->handle = (void *)tp;
+		s->index = index;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * get the ith global symbol
+ */
+int
+globalsym(Symbol *s, int index)
+{
+	if(s == 0)
+		return 0;
+	if(buildtbls() == 0)
+		return 0;
+
+	if(index >=0 && index < nglob) {
+		fillsym(globals[index], s);
+		s->index = index;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ *	find the pc given a file name and line offset into it.
+ */
+uvlong
+file2pc(char *file, int32 line)
+{
+	File *fp;
+	int32 i;
+	uvlong pc, start, end;
+	short *name;
+
+	if(buildtbls() == 0 || files == 0)
+		return ~0;
+	name = encfname(file);
+	if(name == 0) {			/* encode the file name */
+		werrstr("file %s not found", file);
+		return ~0;
+	}
+		/* find this history stack */
+	for(i = 0, fp = files; i < nfiles; i++, fp++)
+		if (hline(fp, name, &line))
+			break;
+	free(name);
+	if(i >= nfiles) {
+		werrstr("line %ld in file %s not found", line, file);
+		return ~0;
+	}
+	start = fp->addr;		/* first text addr this file */
+	if(i < nfiles-1)
+		end = (fp+1)->addr;	/* first text addr next file */
+	else
+		end = 0;		/* last file in load module */
+	/*
+	 * At this point, line contains the offset into the file.
+	 * run the state machine to locate the pc closest to that value.
+	 */
+	if(debug)
+		print("find pc for %ld - between: %llux and %llux\n", line, start, end);
+	pc = line2addr(line, start, end);
+	if(pc == ~0) {
+		werrstr("line %ld not in file %s", line, file);
+		return ~0;
+	}
+	return pc;
+}
+
+/*
+ *	search for a path component index
+ */
+static int
+pathcomp(char *s, int n)
+{
+	int i;
+
+	for(i = 0; i <= fmaxi; i++)
+		if(fnames[i] && strncmp(s, fnames[i]->name, n) == 0)
+			return i;
+	return -1;
+}
+
+/*
+ *	Encode a char file name as a sequence of short indices
+ *	into the file name dictionary.
+ */
+static short*
+encfname(char *file)
+{
+	int i, j;
+	char *cp, *cp2;
+	short *dest;
+
+	if(*file == '/')	/* always check first '/' */
+		cp2 = file+1;
+	else {
+		cp2 = strchr(file, '/');
+		if(!cp2)
+			cp2 = strchr(file, 0);
+	}
+	cp = file;
+	dest = 0;
+	for(i = 0; *cp; i++) {
+		j = pathcomp(cp, cp2-cp);
+		if(j < 0)
+			return 0;	/* not found */
+		dest = realloc(dest, (i+1)*sizeof(short));
+		dest[i] = j;
+		cp = cp2;
+		while(*cp == '/')	/* skip embedded '/'s */
+			cp++;
+		cp2 = strchr(cp, '/');
+		if(!cp2)
+			cp2 = strchr(cp, 0);
+	}
+	dest = realloc(dest, (i+1)*sizeof(short));
+	dest[i] = 0;
+	return dest;
+}
+
+/*
+ *	Search a history stack for a matching file name accumulating
+ *	the size of intervening files in the stack.
+ */
+static int
+hline(File *fp, short *name, int32 *line)
+{
+	Hist *hp;
+	int offset, depth;
+	int32 ln;
+
+	for(hp = fp->hist; hp->name; hp++)		/* find name in stack */
+		if(hp->name[1] || hp->name[2]) {
+			if(hcomp(hp, name))
+				break;
+		}
+	if(!hp->name)		/* match not found */
+		return 0;
+	if(debug)
+		printhist("hline found ... ", hp, 1);
+	/*
+	 * unwind the stack until empty or we hit an entry beyond our line
+	 */
+	ln = *line;
+	offset = hp->line-1;
+	depth = 1;
+	for(hp++; depth && hp->name; hp++) {
+		if(debug)
+			printhist("hline inspect ... ", hp, 1);
+		if(hp->name[1] || hp->name[2]) {
+			if(hp->offset){			/* Z record */
+				offset = 0;
+				if(hcomp(hp, name)) {
+					if(*line <= hp->offset)
+						break;
+					ln = *line+hp->line-hp->offset;
+					depth = 1;	/* implicit pop */
+				} else
+					depth = 2;	/* implicit push */
+			} else if(depth == 1 && ln < hp->line-offset)
+					break;		/* Beyond our line */
+			else if(depth++ == 1)		/* push	*/
+				offset -= hp->line;
+		} else if(--depth == 1)		/* pop */
+			offset += hp->line;
+	}
+	*line = ln+offset;
+	return 1;
+}
+
+/*
+ *	compare two encoded file names
+ */
+static int
+hcomp(Hist *hp, short *sp)
+{
+	uchar *cp;
+	int i, j;
+	short *s;
+
+	cp = (uchar *)hp->name;
+	s = sp;
+	if (*s == 0)
+		return 0;
+	for (i = 1; j = (cp[i]<<8)|cp[i+1]; i += 2) {
+		if(j == 0)
+			break;
+		if(*s == j)
+			s++;
+		else
+			s = sp;
+	}
+	return *s == 0;
+}
+
+/*
+ *	Convert a pc to a "file:line {file:line}" string.
+ */
+int32
+fileline(char *str, int n, uvlong dot)
+{
+	int32 line, top, bot, mid;
+	File *f;
+
+	*str = 0;
+	if(buildtbls() == 0)
+		return 0;
+		/* binary search assumes file list is sorted by addr */
+	bot = 0;
+	top = nfiles;
+	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+		f = &files[mid];
+		if(dot < f->addr)
+			top = mid;
+		else if(mid < nfiles-1 && dot >= (f+1)->addr)
+			bot = mid;
+		else {
+			line = pc2line(dot);
+			if(line > 0 && fline(str, n, line, f->hist, 0) >= 0)
+				return 1;
+			break;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	Convert a line number within a composite file to relative line
+ *	number in a source file.  A composite file is the source
+ *	file with included files inserted in line.
+ */
+static int
+fline(char *str, int n, int32 line, Hist *base, Hist **ret)
+{
+	Hist *start;			/* start of current level */
+	Hist *h;			/* current entry */
+	int32 delta;			/* sum of size of files this level */
+	int k;
+
+	start = base;
+	h = base;
+	delta = h->line;
+	while(h && h->name && line > h->line) {
+		if(h->name[1] || h->name[2]) {
+			if(h->offset != 0) {	/* #line Directive */
+				delta = h->line-h->offset+1;
+				start = h;
+				base = h++;
+			} else {		/* beginning of File */
+				if(start == base)
+					start = h++;
+				else {
+					k = fline(str, n, line, start, &h);
+					if(k <= 0)
+						return k;
+				}
+			}
+		} else {
+			if(start == base && ret) {	/* end of recursion level */
+				*ret = h;
+				return 1;
+			} else {			/* end of included file */
+				delta += h->line-start->line;
+				h++;
+				start = base;
+			}
+		}
+	}
+	if(!h)
+		return -1;
+	if(start != base)
+		line = line-start->line+1;
+	else
+		line = line-delta+1;
+	if(!h->name)
+		strncpy(str, "<eof>", n);
+	else {
+		k = fileelem(fnames, (uchar*)start->name, str, n);
+		if(k+8 < n)
+			sprint(str+k, ":%ld", line);
+	}
+/**********Remove comments for complete back-trace of include sequence
+ *	if(start != base) {
+ *		k = strlen(str);
+ *		if(k+2 < n) {
+ *			str[k++] = ' ';
+ *			str[k++] = '{';
+ *		}
+ *		k += fileelem(fnames, (uchar*) base->name, str+k, n-k);
+ *		if(k+10 < n)
+ *			sprint(str+k, ":%ld}", start->line-delta);
+ *	}
+ ********************/
+	return 0;
+}
+
+/*
+ *	convert an encoded file name to a string.
+ */
+int
+fileelem(Sym **fp, uchar *cp, char *buf, int n)
+{
+	int i, j;
+	char *c, *bp, *end;
+
+	bp = buf;
+	end = buf+n-1;
+	for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
+		c = fp[j]->name;
+		if(bp != buf && bp[-1] != '/' && bp < end)
+			*bp++ = '/';
+		while(bp < end && *c)
+			*bp++ = *c++;
+	}
+	*bp = 0;
+	i =  bp-buf;
+	if(i > 1) {
+		cleanname(buf);
+		i = strlen(buf);
+	}
+	return i;
+}
+
+/*
+ *	compare the values of two symbol table entries.
+ */
+static int
+symcomp(const void *a, const void *b)
+{
+	int i;
+
+	i = (*(Sym**)a)->value - (*(Sym**)b)->value;
+	if (i)
+		return i;
+	return strcmp((*(Sym**)a)->name, (*(Sym**)b)->name);
+}
+
+/*
+ *	compare the values of the symbols referenced by two text table entries
+ */
+static int
+txtcomp(const void *a, const void *b)
+{
+	return ((Txtsym*)a)->sym->value - ((Txtsym*)b)->sym->value;
+}
+
+/*
+ *	compare the values of the symbols referenced by two file table entries
+ */
+static int
+filecomp(const void *a, const void *b)
+{
+	return ((File*)a)->addr - ((File*)b)->addr;
+}
+
+/*
+ *	fill an interface Symbol structure from a symbol table entry
+ */
+static void
+fillsym(Sym *sp, Symbol *s)
+{
+	s->type = sp->type;
+	s->value = sp->value;
+	s->name = sp->name;
+	s->index = 0;
+	switch(sp->type) {
+	case 'b':
+	case 'B':
+	case 'D':
+	case 'd':
+		s->class = CDATA;
+		break;
+	case 't':
+	case 'T':
+	case 'l':
+	case 'L':
+		s->class = CTEXT;
+		break;
+	case 'a':
+		s->class = CAUTO;
+		break;
+	case 'p':
+		s->class = CPARAM;
+		break;
+	case 'm':
+		s->class = CSTAB;
+		break;
+	default:
+		s->class = CNONE;
+		break;
+	}
+	s->handle = 0;
+}
+
+/*
+ *	find the stack frame, given the pc
+ */
+uvlong
+pc2sp(uvlong pc)
+{
+	uchar *c, u;
+	uvlong currpc, currsp;
+
+	if(spoff == 0)
+		return ~0;
+	currsp = 0;
+	currpc = txtstart - mach->pcquant;
+
+	if(pc<currpc || pc>txtend)
+		return ~0;
+	for(c = spoff; c < spoffend; c++) {
+		if (currpc >= pc)
+			return currsp;
+		u = *c;
+		if (u == 0) {
+			currsp += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+			c += 4;
+		}
+		else if (u < 65)
+			currsp += 4*u;
+		else if (u < 129)
+			currsp -= 4*(u-64);
+		else
+			currpc += mach->pcquant*(u-129);
+		currpc += mach->pcquant;
+	}
+	return ~0;
+}
+
+/*
+ *	find the source file line number for a given value of the pc
+ */
+int32
+pc2line(uvlong pc)
+{
+	uchar *c, u;
+	uvlong currpc;
+	int32 currline;
+
+	if(pcline == 0)
+		return -1;
+	currline = 0;
+	if (firstinstr != 0)
+		currpc = firstinstr-mach->pcquant;
+	else
+		currpc = txtstart-mach->pcquant;
+	if(pc<currpc || pc>txtend)
+		return ~0;
+
+	for(c = pcline; c < pclineend; c++) {
+		if(currpc >= pc)
+			return currline;
+		u = *c;
+		if(u == 0) {
+			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+			c += 4;
+		}
+		else if(u < 65)
+			currline += u;
+		else if(u < 129)
+			currline -= (u-64);
+		else
+			currpc += mach->pcquant*(u-129);
+		currpc += mach->pcquant;
+	}
+	return ~0;
+}
+
+/*
+ *	find the pc associated with a line number
+ *	basepc and endpc are text addresses bounding the search.
+ *	if endpc == 0, the end of the table is used (i.e., no upper bound).
+ *	usually, basepc and endpc contain the first text address in
+ *	a file and the first text address in the following file, respectively.
+ */
+uvlong
+line2addr(int32 line, uvlong basepc, uvlong endpc)
+{
+	uchar *c,  u;
+	uvlong currpc, pc;
+	int32 currline;
+	int32 delta, d;
+	int found;
+
+	if(pcline == 0 || line == 0)
+		return ~0;
+
+	currline = 0;
+	currpc = txtstart-mach->pcquant;
+	pc = ~0;
+	found = 0;
+	delta = HUGEINT;
+
+	for(c = pcline; c < pclineend; c++) {
+		if(endpc && currpc >= endpc)	/* end of file of interest */
+			break;
+		if(currpc >= basepc) {		/* proper file */
+			if(currline >= line) {
+				d = currline-line;
+				found = 1;
+			} else
+				d = line-currline;
+			if(d < delta) {
+				delta = d;
+				pc = currpc;
+			}
+		}
+		u = *c;
+		if(u == 0) {
+			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+			c += 4;
+		}
+		else if(u < 65)
+			currline += u;
+		else if(u < 129)
+			currline -= (u-64);
+		else
+			currpc += mach->pcquant*(u-129);
+		currpc += mach->pcquant;
+	}
+	if(found)
+		return pc;
+	return ~0;
+}
+
+/*
+ *	Print a history stack (debug). if count is 0, prints the whole stack
+ */
+static void
+printhist(char *msg, Hist *hp, int count)
+{
+	int i;
+	uchar *cp;
+	char buf[128];
+
+	i = 0;
+	while(hp->name) {
+		if(count && ++i > count)
+			break;
+		print("%s Line: %lx (%ld)  Offset: %lx (%ld)  Name: ", msg,
+			hp->line, hp->line, hp->offset, hp->offset);
+		for(cp = (uchar *)hp->name+1; (*cp<<8)|cp[1]; cp += 2) {
+			if (cp != (uchar *)hp->name+1)
+				print("/");
+			print("%x", (*cp<<8)|cp[1]);
+		}
+		fileelem(fnames, (uchar *) hp->name, buf, sizeof(buf));
+		print(" (%s)\n", buf);
+		hp++;
+	}
+}
+
+#ifdef DEBUG
+/*
+ *	print the history stack for a file. (debug only)
+ *	if (name == 0) => print all history stacks.
+ */
+void
+dumphist(char *name)
+{
+	int i;
+	File *f;
+	short *fname;
+
+	if(buildtbls() == 0)
+		return;
+	if(name)
+		fname = encfname(name);
+	for(i = 0, f = files; i < nfiles; i++, f++)
+		if(fname == 0 || hcomp(f->hist, fname))
+			printhist("> ", f->hist, f->n);
+
+	if(fname)
+		free(fname);
+}
+#endif