Commit Diff


commit - 0e3cc9f456ea49919459bf1164d0c8309a6134fa
commit + a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46
blob - /dev/null
blob + 2838b392b01b5b92934293b71763d1f539ff57f6 (mode 644)
--- /dev/null
+++ src/libmach/FreeBSD.c
@@ -0,0 +1,43 @@
+/*
+ * process interface for FreeBSD 
+ */
+
+#include <u.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <libc.h>
+#include <mach.h>
+#include "ureg386.h"
+
+void
+unmapproc(Map*)
+{
+}
+
+int
+mapproc(int, Map*, Regs**)
+{
+}
+
+int
+detachproc(int)
+{
+}
+
+int
+procnotes(int, char***)
+{
+}
+
+int
+ctlproc(int, char*)
+{
+}
+
+char*
+proctextfile(int)
+{
+}
blob - /dev/null
blob + 62796adbce1b346fede9efadda2ff75cd642010b (mode 644)
--- /dev/null
+++ src/libmach/Linux.c
@@ -0,0 +1,450 @@
+/*
+ * process interface for Linux.
+ * 
+ * Uses ptrace for registers and data,
+ * /proc for some process status.
+ * There's not much point to worrying about
+ * byte order here -- using ptrace means
+ * we're running on the architecture we're debugging,
+ * unless truly weird stuff is going on.
+ *
+ * It is tempting to use /proc/%d/mem along with
+ * the sp and pc in the stat file to get a stack trace
+ * without attaching to the program, but unfortunately
+ * you can't read the mem file unless you've attached.
+ */
+
+#include <u.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <libc.h>
+#include <mach.h>
+#include "ureg386.h"
+
+Mach *machcpu = &mach386;
+
+typedef struct PtraceRegs PtraceRegs;
+
+struct PtraceRegs
+{
+	Regs r;
+	int pid;
+};
+
+static int ptracerw(Map*, Seg*, ulong, void*, uint, int);
+static int ptraceregrw(Regs*, char*, ulong*, int);
+
+void
+unmapproc(Map *map)
+{
+	int i;
+
+	if(map == nil)
+		return;
+	for(i=0; i<map->nseg; i++)
+		while(i<map->nseg && map->seg[i].pid){
+			map->nseg--;
+			memmove(&map->seg[i], &map->seg[i+1], 
+				(map->nseg-i)*sizeof(map->seg[0]));
+		}
+}
+
+int
+mapproc(int pid, Map *map, Regs **rp)
+{
+	Seg s;
+	PtraceRegs *r;
+
+	if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0)
+	if(ptrace(PTRACE_PEEKUSER, pid, 0, 0) < 0)
+	if(ptrace(PTRACE_ATTACH, pid, 0, 0) < 0){
+		werrstr("ptrace attach %d: %r", pid);
+		return -1;
+	}
+
+	if(ctlproc(pid, "waitstop") < 0){
+		ptrace(PTRACE_DETACH, pid, 0, 0);
+		return -1;
+	}
+
+	memset(&s, 0, sizeof s);
+	s.base = 0;
+	s.size = 0xFFFFFFFF;
+	s.offset = 0;
+	s.name = "data";
+	s.file = nil;
+	s.rw = ptracerw;
+	s.pid = pid;
+	if(addseg(map, s) < 0)
+		return -1;
+
+	if((r = mallocz(sizeof(PtraceRegs), 1)) == nil)
+		return -1;
+	r->r.rw = ptraceregrw;
+	r->pid = pid;
+	*rp = (Regs*)r;
+	return 0;
+}
+
+int
+detachproc(int pid)
+{
+	return ptrace(PTRACE_DETACH, pid, 0, 0);
+}
+
+static int
+ptracerw(Map *map, Seg *seg, ulong addr, void *v, uint n, int isr)
+{
+	int i;
+	u32int u;
+	uchar buf[4];
+
+	addr += seg->base;
+	for(i=0; i<n; i+=4){
+		if(isr){
+			errno = 0;
+			u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
+			if(errno)
+				goto ptraceerr;
+			if(n-i >= 4)
+				*(u32int*)((char*)v+i) = u;
+			else{
+				*(u32int*)buf = u;
+				memmove((char*)v+i, buf, n-i);
+			}
+		}else{
+			if(n-i >= 4)
+				u = *(u32int*)((char*)v+i);
+			else{
+				errno = 0;
+				u = ptrace(PTRACE_PEEKDATA, seg->pid, addr+i, 0);
+				if(errno)
+					return -1;
+				*(u32int*)buf = u;
+				memmove(buf, (char*)v+i, n-i);
+				u = *(u32int*)buf;
+			}
+			if(ptrace(PTRACE_POKEDATA, seg->pid, addr+i, &u) < 0)
+				goto ptraceerr;
+		}
+	}
+	return 0;
+
+ptraceerr:
+	werrstr("ptrace: %r");
+	return -1;
+}
+
+static char* linuxregs[] = {
+	"BX",
+	"CX",
+	"DX",
+	"SI",
+	"DI",
+	"BP",
+	"AX",
+	"DS",
+	"ES",
+	"FS",
+	"GS",
+	"OAX",
+	"PC",
+	"CS",
+	"EFLAGS",
+	"SP",
+	"SS",
+};
+
+static ulong
+reg2linux(char *reg)
+{
+	int i;
+
+	for(i=0; i<nelem(linuxregs); i++)
+		if(strcmp(linuxregs[i], reg) == 0)
+			return 4*i;
+	return ~(ulong)0;
+}
+
+static int
+ptraceregrw(Regs *regs, char *name, ulong *val, int isr)
+{
+	int pid;
+	ulong addr;
+	u32int u;
+
+	pid = ((PtraceRegs*)regs)->pid;
+	addr = reg2linux(name);
+	if(~addr == 0){
+		if(isr){
+			*val = ~(ulong)0;
+			return 0;
+		}
+		werrstr("register not available");
+		return -1;
+	}
+	if(isr){
+		errno = 0;
+		u = ptrace(PTRACE_PEEKUSER, pid, addr, 0);
+		if(errno)
+			goto ptraceerr;
+		*val = u;
+	}else{
+		u = *val;
+		if(ptrace(PTRACE_POKEUSER, pid, addr, &u) < 0)
+			goto ptraceerr;
+	}
+	return 0;
+
+ptraceerr:
+	werrstr("ptrace: %r");
+	return -1;
+}
+
+static int
+isstopped(int pid)
+{
+	char buf[1024];
+	int fd, n;
+	char *p;
+
+	snprint(buf, sizeof buf, "/proc/%d/stat", pid);
+	if((fd = open(buf, OREAD)) < 0)
+		return 0;
+	n = read(fd, buf, sizeof buf-1);
+	close(fd);
+	if(n <= 0)
+		return 0;
+	buf[n] = 0;
+
+	/* command name is in parens, no parens afterward */
+	p = strrchr(buf, ')');
+	if(p == nil || *++p != ' ')
+		return 0;
+	++p;
+
+	/* next is state - T is stopped for tracing */
+	return *p == 'T';
+}
+
+/* /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
+*/
+
+
+int
+procnotes(int pid, char ***pnotes)
+{
+	char buf[1024], *f[40];
+	int fd, i, n, nf;
+	char *p, *s, **notes;
+	ulong sigs;
+	extern char *_p9sigstr(int, char*);
+
+	*pnotes = nil;
+	snprint(buf, sizeof buf, "/proc/%d/stat", pid);
+	if((fd = open(buf, OREAD)) < 0){
+		fprint(2, "open %s: %r\n", buf);
+		return -1;
+	}
+	n = read(fd, buf, sizeof buf-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, nelem(f));
+	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));
+	if(nf <= 28)
+		return -1;
+
+	sigs = strtoul(f[28], 0, 0) & ~(1<<SIGCONT);
+	if(sigs == 0){
+		*pnotes = nil;
+		return 0;
+	}
+
+	notes = mallocz(32*sizeof(char*), 0);
+	if(notes == nil)
+		return -1;
+	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;
+}
+
+#undef waitpid
+
+int
+ctlproc(int pid, char *msg)
+{
+	int p, status;
+
+	if(strcmp(msg, "hang") == 0){
+		if(pid == getpid())
+			return ptrace(PTRACE_TRACEME, 0, 0, 0);
+		werrstr("can only hang self");
+		return -1;
+	}
+	if(strcmp(msg, "kill") == 0)
+		return ptrace(PTRACE_KILL, pid, 0, 0);
+	if(strcmp(msg, "startstop") == 0){
+		if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
+			return -1;
+		goto waitstop;
+	}
+	if(strcmp(msg, "sysstop") == 0){
+		if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
+			return -1;
+		goto waitstop;
+	}
+	if(strcmp(msg, "stop") == 0){
+		if(kill(pid, SIGSTOP) < 0)
+			return -1;
+		goto waitstop;
+	}
+	if(strcmp(msg, "waitstop") == 0){
+	waitstop:
+		if(isstopped(pid))
+			return 0;
+		for(;;){
+			p = waitpid(pid, &status, WUNTRACED);
+			if(p <= 0)
+				return -1;
+			if(WIFEXITED(status) || WIFSTOPPED(status))
+				return 0;
+		}
+	}
+	if(strcmp(msg, "start") == 0)
+		return ptrace(PTRACE_CONT, pid, 0, 0);
+	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 buf;
+	if(access(pbuf, AEXIST) >= 0)
+		return pbuf;
+	return nil;
+}
+
+
+#if 0
+	snprint(buf, sizeof buf, "/proc/%d/maps", pid);
+	if((b = Bopen(buf, OREAD)) == nil){
+		werrstr("open %s: %r", buf);
+		return -1;
+	}
+
+/*
+        08048000-08056000 r-xp 00000000 03:0c 64593      /usr/sbin/gpm
+        08056000-08058000 rw-p 0000d000 03:0c 64593      /usr/sbin/gpm
+        08058000-0805b000 rwxp 00000000 00:00 0
+        40000000-40013000 r-xp 00000000 03:0c 4165       /lib/ld-2.2.4.so
+        40013000-40015000 rw-p 00012000 03:0c 4165       /lib/ld-2.2.4.so
+        4001f000-40135000 r-xp 00000000 03:0c 45494      /lib/libc-2.2.4.so
+        40135000-4013e000 rw-p 00115000 03:0c 45494      /lib/libc-2.2.4.so
+        4013e000-40142000 rw-p 00000000 00:00 0
+        bffff000-c0000000 rwxp 00000000 00:00 0
+*/
+
+	file = nil;
+	while((p = Brdline(b, '\n')) != nil){
+		p[Blinelen(b)-1] = 0;
+		memset(f, 0, sizeof f);
+		if((nf = getfields(p, f, 6, 1, " ")) < 5)
+			continue;
+		base = strtoul(f[0], &p, 16);
+		if(*p != '-')
+			continue;
+		end = strtoul(p+1, &p, 16);
+		if(*p != 0)
+			continue;
+		offset = strtoul(f[2], &p, 16);
+		if(*p != 0)
+			continue;
+		if(nf == 6)
+			file = f[5];
+		zero = atoi(f[4]) == 0;
+		print("%lux-%lux %lux %s %s\n", base, end, offset, zero ? "data" : "text", file ? file : "");
+		s.base = base;
+		s.size = end - base;
+		s.offset = offset;
+		s.name = zero ? "data" : "text";
+		s.file = strdup(file);
+		s.rw = ptracerw;
+		s.pid = pid;
+		if(addseg(map, s) < 0){
+			Bterm(b);
+			ptrace(PTRACE_DETACH, pid, 0, 0);
+			return -1;
+		}
+	}
+	Bterm(b);
+#endif
+
blob - /dev/null
blob + ea8a808cd82d49985ff9a8143e8bc05b164df7dc (mode 644)
--- /dev/null
+++ src/libmach/Notes
@@ -0,0 +1,13 @@
+
+TODO ===============
+
+Move 386 crap out of symdwarf.c, symstabs.c.
+Implement line2pc in dwarf.c.
+Parse ELF .dynamic section for a few more symbols.
+
+Add stabs support to acidtypes.
+Handle multiple symbol files better in acid.
+Write acid code to walk dynamic linking tables.
+
+Write lax command-line parsing code for acid, db.
+
blob - /dev/null
blob + 78046f5007de7850b10c22115cb04bf8b8508f22 (mode 644)
--- /dev/null
+++ src/libmach/Notes.stab
@@ -0,0 +1,166 @@
+stabs
+
+N_MAIN with name "main" to identify entry function
+N_SO source file.  might be preceded by dir ending in /
+	value is code ptr
+	empty string means source done
+N_SOL source include file, value = text addr of where this starts
+N_SLINE start of source line
+	no name
+	desc = line number
+	value = code addr for that line
+
+N_FUN (36) function def
+	'F' global 'f' local
+	value = addr
+	desc = line number of def
+	return type is number after :
+
+	nil name marks end of function
+
+constants
+
+	c= XXX p. 15
+
+N_LSYM (128) local variable
+	:type-number
+	value = offset from fp
+	:ptype means parameter passed in reg but stored as variable
+
+N_GSYM (32) global variable
+	addr not given (use external symbol)
+	:type
+
+N_RSYM register value
+
+N_STSYM(38)/N_FUN/N_LCSYM(40) data/text/bss
+	static varibale 'S' file static 'V' procedure static
+	:Stype :Vtype
+
+N_PSYM (160) parameter 
+	:ptype
+	value=offset from fp
+	desc = line number of decl
+
+	register params followed by an N_RSYM with same name and :rtype.
+
+skip types
+
+type (a,b) means a=file number b=type number
+
+N_BINCL/N_EINCL begin/end include
+N_EXCL - same effect as earlier guy
+
+
+
+=============
+
+type crap
+
+
+
+	name:symbol_opt typeinfo
+
+typeinfo ::= typenum | typenum = attr* typedef
+
+typenum ::= integer | '(' integer ',' integer ')'
+
+attr ::= @ attrtext ;
+
+attrtext ::= 'a' integer 	(alignment)
+	| 'p' integer	(pointer class)
+	| 'P'	(packed type)
+	| 's' integer	(size of type in bits)
+	| 'S' 	(string instead of array of chars)
+
+typedef ::= typeinfo
+	| 'b' ('u' | 's') 'c'? width; offset; nbits; 		(builtin, signed/unsigned, char/not, width in bytes, offset & nbits of type)
+	| 'w'		(aix wide char type, not used)
+	| 'R' fptype; bytes;			(fptype 1=32-bit, 2=64-bit, 3=complex, 4=complex16, 5=complex32, 6=long double)
+	| 'g' typeinfo ';' nbits 		(aix floating, not used)
+	| 'c' typeinfo ';' nbits		(aix complex, not used)
+	| -1		int32
+	| -2		char8
+	| -3		int16
+	| -4		int32 (long)
+	| -5		uchar8
+	| -6		schar8
+	| -7		uint16
+	| -8		uint32
+	| -9		uint32
+	| -10		ulong32
+	| -11		void
+	| -12		float
+	| -13		double
+	| -14		long double
+	| -15		int32
+	| -16		bool32
+	| -17		short real
+	| -18		real
+	| -19		stringptr
+	| -20		character8
+	| -21		logical*1 8
+	| -22		logical*2 16
+	| -23		logical*4 32
+	| -24		logical 32
+	| -25		complex (two single)
+	| -26		complex (two double)
+	| -27		integer*1 8 signed
+	| -28		integer*2 16 signed
+	| -29		integer*4 32 signed
+	| -30		wchar 16 wide char
+	| -31		int64
+	| -32		uint64
+	| -33		logical*8 64
+	| -34		integer*8 64 signed
+	| 'b' typeinfo ';' bytes		(ibm, no idea)
+	| 'B' typeinfo		(volatile typref)
+	| 'd' typeinfo		(file of typeref)
+	| 'k' typeinfo		(const typeref)
+	| 'M' typeinfo ';' length		(multiple instance type, fortran)
+	| 'S' typeinfo		(set, typeref must have small number of values)
+	| '*' typeinfo		(pointer to typeref)
+	| 'x' ('s'|'u'|'e') name ':'	(struct, union, enum reference.  name can have '::' in it)
+	| 'r' typeinfo ';' low ';' high ';'		(subrange.  typeref can be type being defined for base types!)
+			low and high are bounds
+			if bound is octal power of two, it's a big negative number
+	| ('a'|'P') indextypedef arraytypeinfo 	(array, index should be range type)
+				indextype is type definition not typeinfo (need not say typenum=)
+				P means packed array
+	| 'A' arraytypeinfo		(open array (no index bounds))
+	| 'D' dims ';' typeinfo		(dims-dimensional dynamic array)
+	| 'E' dims ';' typeinfo		(subarray of N-dimensional array)
+	| 'n' typeinfo ';' bytes		(max length string)
+	| 'z' typeinfo ';' bytes		(no idea what difference is from 'n')
+	| 'N'					(pascal stringptr)
+	| 'e' (name ':' bigint ',')* ';'	(enum listing)
+	| ('s'|'u') bytes (name ':' type ',' bitoffset ',' bitsize ';')* ';'		(struct/union defn)
+						tag is given as name in stabs entry, with 'T' symbol
+	| 'f' typeinfo ';' 			(function returning type)
+	| 'f' rettypeinfo ',' paramcount ';' (typeinfo ',' (0|1) ';')* ';'
+	| 'p' paramcount ';' (typeinfo ',' (0|1) ';')* ';'
+	| 'F' rettypeinfo ',' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';'
+	| 'R' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';'
+						(the 0 or 1 is pass-by-reference vs pass-by-value)
+						(the 0 or 1 is pass-by-reference vs pass-by-value)
+
+bound ::= 
+	'A' offset			(bound is on stack by ref at offset offset from arg list)
+	| 'T' offset			(bound is on stack by val at offset offset from arg list)
+	| 'a' regnum		(bound passed by reference in register)
+	| 't' regnum		(bound passed by value in register)
+	| 'J'				(no bound)
+	| bigint
+
+bigint ::= '-'? decimal
+	| 0 octal
+	| -1
+
+C++
+
+symbol 'Tt' means typename + tag in one stab
+
+names can have ::, as in foo::bar::baz::t1
+
+t16 unknown type just like void
+t17 vtable record type
blob - /dev/null
blob + 756518971e3d07ff818fec62e862f6d513227fc1 (mode 644)
--- /dev/null
+++ src/libmach/crack.c
@@ -0,0 +1,91 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+static struct
+{
+	ulong magic;
+	int (*fn)(int, Fhdr*);
+} cracktab[] = {
+	0x7F454C46,	crackelf,
+	0xFEEDFACE,	crackmacho,
+};
+
+Fhdr*
+crackhdr(char *name, int mode)
+{
+	uchar buf[4];
+	ulong magic;
+	int i, fd;
+	Fhdr *hdr;
+
+	if((fd = open(name, mode)) < 0)
+		return nil;
+
+	if(seek(fd, 0, 0) < 0 || readn(fd, buf, 4) != 4){
+		close(fd);
+		return nil;
+	}
+
+	hdr = mallocz(sizeof(Fhdr), 1);
+	if(hdr == nil){
+		close(fd);
+		return nil;
+	}
+	hdr->filename = strdup(name);
+	magic = beload4(buf);
+	werrstr("magic doesn't match");
+	for(i=0; i<nelem(cracktab); i++)
+		if(cracktab[i].magic == magic && seek(fd, 0, 0) == 0 && cracktab[i].fn(fd, hdr) >= 0){
+			_addhdr(hdr);
+			return hdr;
+		}
+	werrstr("unknown file type: %r");
+	free(hdr);
+	close(fd);
+	return nil;
+}
+
+void
+uncrackhdr(Fhdr *hdr)
+{
+	close(hdr->fd);
+	_delhdr(hdr);
+	free(hdr);
+}
+
+int
+mapfile(Fhdr *fp, ulong base, Map *map, Regs **regs)
+{
+	if(fp == nil){
+		werrstr("no file");
+		return -1;
+	}
+	if(map == nil){
+		werrstr("no map");
+		return -1;
+	}
+	if(fp->map == nil){
+		werrstr("cannot load map for this file type");
+		return -1;
+	}
+	return fp->map(fp, base, map, regs);
+}
+
+void
+unmapfile(Fhdr *fp, Map *map)
+{
+	int i;
+
+	if(map == nil || fp == nil)
+		return;
+
+	for(i=0; i<map->nseg; i++){
+		while(i<map->nseg && map->seg[i].fd == fp->fd){
+			map->nseg--;
+			memmove(&map->seg[i], &map->seg[i+1], 
+				(map->nseg-i)*sizeof(map->seg[0]));
+		}
+	}
+}
blob - /dev/null
blob + 0d15804bb7a43e2e558ca2d367f3d07e2d150a82 (mode 644)
--- /dev/null
+++ src/libmach/crackelf.c
@@ -0,0 +1,342 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int mapelf(Fhdr *fp, ulong base, Map *map, Regs**);
+static int mapcoreregs(Fhdr *fp, Map *map, Regs**);
+
+static struct
+{
+	uint etype;
+	uint mtype;
+	Mach *mach;
+	char *name;
+} mtab[] = 
+{	/* Font Tab 4 */
+	ElfMachSparc,	MSPARC,		nil,		"sparc",
+	ElfMach386,		M386,		&mach386,	"386",
+	ElfMachMips,	MMIPS,		nil,		"mips",
+	ElfMachArm,		MARM,		nil,		"arm",
+	ElfMachPower,	MPOWER,		nil,		"powerpc",
+	ElfMachPower64,	MNONE,		nil,		"powerpc64",
+};
+
+static struct
+{
+	uint etype;
+	uint atype;
+	char *aname;
+} atab[] = 
+{	/* Font Tab 4 */
+	ElfAbiSystemV,	ALINUX,		"linux",	/* [sic] */
+	ElfAbiLinux,	ALINUX,		"linux",
+	ElfAbiFreeBSD,	AFREEBSD,	"freebsd",
+};
+
+static struct
+{
+	uint mtype;
+	uint atype;
+	int (*coreregs)(Elf*, ElfNote*, uchar**);
+} ctab[] = 
+{	/* Font Tab 4 */
+	M386,		ALINUX,		coreregslinux386,
+	M386,		ANONE,		coreregslinux386,	/* [sic] */
+	M386,		AFREEBSD,	coreregsfreebsd386,
+};
+
+int
+crackelf(int fd, Fhdr *fp)
+{
+	int i, havetext, havedata;
+	Elf *elf;
+	ElfProg *p;
+	ElfSect *s1, *s2;
+
+	if((elf = elfinit(fd)) == nil)
+		return -1;
+
+	fp->fd = fd;
+	fp->elf = elf;
+	fp->dwarf = dwarfopen(elf);	/* okay to fail */
+	fp->syminit = symelf;
+
+	if((s1 = elfsection(elf, ".stab")) != nil && s1->link!=0 && s1->link < elf->nsect){
+		s2 = &elf->sect[s1->link];
+		if(elfmap(elf, s1) >= 0 && elfmap(elf, s2) >= 0){
+			fp->stabs.stabbase = s1->base;
+			fp->stabs.stabsize = s1->size;
+			fp->stabs.strbase = s2->base;
+			fp->stabs.strsize = s2->size;
+			fp->stabs.e2 = elf->hdr.e2;
+			fp->stabs.e4 = elf->hdr.e4;
+		}
+	}
+
+	for(i=0; i<nelem(mtab); i++){
+		if(elf->hdr.machine != mtab[i].etype)
+			continue;
+		fp->mach = mtab[i].mach;
+		fp->mname = mtab[i].name;
+		fp->mtype = mtab[i].mtype;
+		break;
+	}
+	if(i == nelem(mtab)){
+		werrstr("unsupported machine type %d", elf->hdr.machine);
+		goto err;
+	}
+
+	if(mach == nil)
+		mach = fp->mach;
+
+	fp->aname = "unknown";
+	for(i=0; i<nelem(atab); i++){
+		if(elf->hdr.abi != atab[i].etype)
+			continue;
+		fp->atype = atab[i].atype;
+		fp->aname = atab[i].aname;
+		break;
+	}
+
+	switch(elf->hdr.type){
+	default:
+		werrstr("unknown file type %d", elf->hdr.type);
+		goto err;
+	case ElfTypeExecutable:
+		fp->ftype = FEXEC;
+		fp->fname = "executable";
+		break;
+	case ElfTypeRelocatable:
+		fp->ftype = FRELOC;
+		fp->fname = "relocatable";
+		break;
+	case ElfTypeSharedObject:
+		fp->ftype = FSHOBJ;
+		fp->fname = "shared object";
+		break;
+	case ElfTypeCore:
+		fp->ftype = FCORE;
+		fp->fname = "core dump";
+		break;
+	}
+
+	fp->map = mapelf;
+
+	if(fp->ftype == FCORE){
+		for(i=0; i<nelem(ctab); i++){
+			if(ctab[i].atype != fp->atype
+			|| ctab[i].mtype != fp->mtype)
+				continue;
+			elf->coreregs = ctab[i].coreregs;
+			break;
+		}
+		return 0;
+	}
+
+	fp->entry = elf->hdr.entry;
+
+	/* First r-x section we find is the text and initialized data */
+	/* First rw- section we find is the r/w data */
+	havetext = 0;
+	havedata = 0;
+	for(i=0; i<elf->nprog; i++){
+		p = &elf->prog[i];
+		if(p->type != ElfProgLoad)
+			continue;
+		if(!havetext && p->flags == (ElfProgFlagRead|ElfProgFlagExec) && p->align >= mach->pgsize){
+			havetext = 1;
+			fp->txtaddr = p->vaddr;
+			fp->txtsz = p->memsz;
+			fp->txtoff = p->offset;
+		}
+		if(!havedata && p->flags == (ElfProgFlagRead|ElfProgFlagWrite) && p->align >= mach->pgsize){
+			havedata = 1;
+			fp->dataddr = p->vaddr;
+			fp->datsz = p->filesz;
+			fp->datoff = p->offset;
+			fp->bsssz = p->memsz - p->filesz;
+		}
+	}
+	if(!havetext){
+		werrstr("did not find text segment in elf binary");
+		goto err;
+	}
+	if(!havedata){
+		werrstr("did not find data segment in elf binary");
+		goto err;
+	}
+	return 0;
+
+err:
+	elfclose(elf);
+	return -1;
+}
+
+static int
+mapelf(Fhdr *fp, ulong base, Map *map, Regs **regs)
+{
+	int i;
+	Elf *elf;
+	ElfProg *p;
+	ulong sz;
+	ulong lim;
+	Seg s;
+
+	elf = fp->elf;
+	if(elf == nil){
+		werrstr("not an elf file");
+		return -1;
+	}
+
+	for(i=0; i<elf->nprog; i++){
+		p = &elf->prog[i];
+		if(p->type != ElfProgLoad)
+			continue;
+		if(p->align < mach->pgsize)
+			continue;
+		if(p->filesz){
+			memset(&s, 0, sizeof s);
+			s.file = fp->filename;
+			s.fd = fp->fd;
+			if(fp->ftype == FCORE)
+				s.name = "core";
+			else if(p->flags == 5)
+				s.name = "text";
+			else
+				s.name = "data";
+			s.base = base+p->vaddr;
+			s.size = p->filesz;
+			s.offset = p->offset;
+			if(addseg(map, s) < 0)
+				return -1;
+		}
+		/*
+		 * If memsz > filesz, we're supposed to zero fill.
+		 * Core files have zeroed sections where the pages
+		 * can be filled in from the text file, so if this is a core
+		 * we only fill in that which isn't yet mapped.
+		 */
+		if(fp->ftype == FCORE){
+			sz = p->filesz;
+			while(sz < p->memsz){
+				if(addrtoseg(map, base+p->vaddr+sz, &s) < 0){
+					lim = base + p->vaddr + p->memsz;
+					if(addrtosegafter(map, base+p->vaddr+sz, &s) >= 0 && s.base < lim)
+						lim = s.base;
+					memset(&s, 0, sizeof s);
+					s.name = "zero";
+					s.base = base + p->vaddr + sz;
+					s.size = lim - s.base;
+					s.offset = p->offset;
+					if(addseg(map, s) < 0)
+						return -1;
+				}else
+					sz = (s.base+s.size) - (base + p->vaddr);
+			}
+		}else{
+			if(p->filesz < p->memsz){
+				memset(&s, 0, sizeof s);
+				s.name = "zero";
+				s.base = base + p->vaddr + p->filesz;
+				s.size = p->memsz - p->filesz;
+				if(addseg(map, s) < 0)
+					return -1;
+			}
+		}			
+	}
+
+	if(fp->ftype == FCORE){
+		if(mapcoreregs(fp, map, regs) < 0)
+			fprint(2, "warning: reading core regs: %r");
+	}
+
+	return 0;	
+}
+
+static int
+unpacknote(Elf *elf, uchar *a, uchar *ea, ElfNote *note, uchar **pa)
+{
+	if(a+12 > ea)
+		return -1;
+	note->namesz = elf->hdr.e4(a);
+	note->descsz = elf->hdr.e4(a+4);
+	note->type = elf->hdr.e4(a+8);
+	a += 12;
+	note->name = (char*)a;
+/* XXX fetch alignment constants from elsewhere */
+	a += (note->namesz+3)&~3;
+	note->desc = (uchar*)a;
+	a += (note->descsz+3)&~3;
+	if(a > ea)
+		return -1;
+	*pa = a;
+	return 0;
+}
+
+static int
+mapcoreregs(Fhdr *fp, Map *map, Regs **rp)
+{
+	int i;
+	uchar *a, *sa, *ea, *uregs;
+	uint n;
+	ElfNote note;
+	ElfProg *p;
+	Elf *elf;
+	UregRegs *r;
+
+	elf = fp->elf;
+	if(elf->coreregs == nil){
+		werrstr("cannot parse %s %s cores", fp->mname, fp->aname);
+		return -1;
+	}
+
+	for(i=0; i<elf->nprog; i++){
+		p = &elf->prog[i];
+		if(p->type != ElfProgNote)
+			continue;
+		n = p->filesz;
+		a = malloc(n);
+		if(a == nil)
+			return -1;
+		if(seek(fp->fd, p->offset, 0) < 0 || readn(fp->fd, a, n) != n){
+			free(a);
+			continue;
+		}
+		sa = a;
+		ea = a+n;
+		while(a < ea){
+			note.offset = (a-sa) + p->offset;
+			if(unpacknote(elf, a, ea, &note, &a) < 0)
+				break;
+			switch(note.type){
+			case ElfNotePrStatus:
+				if((n = (*elf->coreregs)(elf, &note, &uregs)) < 0){
+					free(sa);
+					return -1;
+				}
+				free(sa);
+				if((r = mallocz(sizeof(*r), 1)) == nil){
+					free(uregs);
+					return -1;
+				}
+				r->r.rw = _uregrw;
+				r->ureg = uregs;
+				*rp = &r->r;
+				return 0;
+			case ElfNotePrFpreg:
+			case ElfNotePrPsinfo:
+			case ElfNotePrTaskstruct:
+			case ElfNotePrAuxv:
+			case ElfNotePrXfpreg:
+				break;
+			}
+		//	fprint(2, "0x%lux note %s/%lud %p\n", note.offset, note.name, note.type, note.desc);
+		}
+		free(sa);
+	}
+	fprint(2, "could not find registers in core file\n");
+	return -1;
+}
+
blob - /dev/null
blob + bfc0fd6211f63588c50e90023411333625019afd (mode 644)
--- /dev/null
+++ src/libmach/crackmacho.c
@@ -0,0 +1,198 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+
+static int mapmacho(Fhdr *fp, ulong base, Map *map, Regs**);
+
+static struct
+{
+	uint etype;
+	uint mtype;
+	Mach *mach;
+	char *name;
+	int (*coreregs)(Macho*, uchar**);
+} mtab[] = 
+{
+	MachoCpuPower,	MPOWER,		&machpower, 		"powerpc",	coreregsmachopower,
+};
+
+static uchar*
+load(int fd, ulong off, int size)
+{
+	uchar *a;
+
+	a = malloc(size);
+	if(a == nil)
+		return nil;
+	if(seek(fd, off, 0) < 0 || readn(fd, a, size) != size){
+		free(a);
+		return nil;
+	}
+	return a;
+}
+
+int
+crackmacho(int fd, Fhdr *fp)
+{
+	int i;
+	Macho *m;
+
+	if((m = machoinit(fd)) == nil)
+		return -1;
+
+	fp->fd = fd;
+	fp->macho = m;
+
+	for(i=0; i<nelem(mtab); i++){
+		if(m->cputype != mtab[i].etype)
+			continue;
+		fp->mach = mtab[i].mach;
+		fp->mtype = mtab[i].mtype;
+		fp->mname = mtab[i].name;
+		m->coreregs = mtab[i].coreregs;
+		break;
+	}
+	if(i == nelem(mtab)){
+		werrstr("unsupported cpu type %ud", m->cputype);
+		goto err;
+	}
+
+	fp->atype = AMACH;
+	fp->aname = "mach";
+
+	if(mach == nil)
+		mach = fp->mach;
+
+	switch(m->filetype){
+	default:
+		werrstr("unsupported macho file type %lud", m->filetype);
+		goto err;
+	case MachoFileObject:
+		fp->ftype = FOBJ;
+		fp->fname = "object";
+		break;
+	case MachoFileExecutable:
+		fp->ftype = FEXEC;
+		fp->fname = "executable";
+		break;
+	case MachoFileFvmlib:
+		fp->ftype = FSHLIB;
+		fp->fname = "shared library";
+		break;
+	case MachoFileCore:
+		fp->ftype = FCORE;
+		fp->fname = "core";
+		break;
+	case MachoFilePreload:
+		fp->ftype = FBOOT;
+		fp->fname = "preloaded executable";
+		break;
+	}
+
+	fp->txtaddr = fp->dataddr = 0;
+	fp->txtsz = fp->datsz = 0;
+	for(i=0; i<m->ncmd; i++){
+		if(m->cmd[i].type != MachoCmdSegment)
+			continue;
+		if(strcmp(m->cmd[i].seg.name, "__TEXT") == 0){
+			fp->txtaddr = m->cmd[i].seg.vmaddr;
+			fp->txtsz = m->cmd[i].seg.vmsize;
+			fp->txtoff = m->cmd[i].seg.fileoff;
+		}
+		if(strcmp(m->cmd[i].seg.name, "__DATA") == 0){
+			fp->dataddr = m->cmd[i].seg.vmaddr;
+			fp->datsz = m->cmd[i].seg.filesz;
+			fp->datoff = m->cmd[i].seg.fileoff;
+			fp->bsssz = m->cmd[i].seg.vmsize - fp->datsz;
+		}
+	}
+
+	fp->map = mapmacho;
+	fp->syminit = symmacho;
+
+	for(i=0; i<m->ncmd; i++)
+		if(m->cmd[i].type == MachoCmdSymtab)
+			break;
+	if(i < m->ncmd){
+		fp->stabs.stabbase = load(fp->fd, m->cmd[i].sym.symoff, m->cmd[i].sym.nsyms*16);
+		fp->stabs.stabsize = m->cmd[i].sym.nsyms*16;
+		fp->stabs.strbase = load(fp->fd, m->cmd[i].sym.stroff, m->cmd[i].sym.strsize);
+		if(fp->stabs.stabbase == nil || fp->stabs.strbase == nil){
+			fp->stabs.stabbase = nil;
+			fp->stabs.strbase = nil;
+		}else{
+			fp->stabs.strsize = m->cmd[i].sym.strsize;
+			fp->stabs.e2 = (m->e4==beload4 ? beload2 : leload2);
+			fp->stabs.e4 = m->e4;
+		}
+	}
+
+	return 0;
+
+err:
+	machoclose(m);
+	return -1;
+}
+
+static int
+mapmacho(Fhdr *fp, ulong base, Map *map, Regs **rp)
+{
+	int i, n;
+	uchar *u;
+	Macho *m;
+	MachoCmd *c;
+	Seg s;
+	UregRegs *r;
+
+	m = fp->macho;
+	if(m == nil){
+		werrstr("not a macho file");
+		return -1;
+	}
+
+	for(i=0; i<m->ncmd; i++){
+		c = &m->cmd[i];
+		if(c->type != MachoCmdSegment)
+			continue;
+		if(c->seg.filesz){
+			memset(&s, 0, sizeof s);
+			s.file = fp->filename;
+			s.fd = fp->fd;
+			if(fp->ftype == FCORE)
+				s.name = "core";
+			else if(strcmp(c->seg.name, "__DATA") == 0)
+				s.name = "data";
+			else
+				s.name = "text";
+			s.base = base+c->seg.vmaddr;
+			s.size = c->seg.filesz;
+			s.offset = c->seg.fileoff;
+			if(addseg(map, s) < 0)
+				return -1;
+		}
+		if(c->seg.filesz < c->seg.vmsize){
+			memset(&s, 0, sizeof s);
+			s.name = "zero";
+			s.base = base + c->seg.vmaddr + c->seg.filesz;
+			s.size = c->seg.vmsize - c->seg.filesz;
+			if(addseg(map, s) < 0)
+				return -1;
+		}
+	}
+
+	if(fp->ftype == FCORE && m->coreregs){
+		n = m->coreregs(m, &u);
+		if(n < 0){
+			fprint(2, "mapping registers: %r\n");
+			goto noregs;
+		}
+		if((r = mallocz(sizeof *r, 1)) == nil)
+			return -1;
+		r->r.rw = _uregrw;
+		r->ureg = u;	
+		*rp = &r->r;
+	}
+noregs:
+	return 0;
+}
blob - /dev/null
blob + 0836523930951a40a383002be9b2aff438c4fa9e (mode 644)
--- /dev/null
+++ src/libmach/dwarf.h
@@ -0,0 +1,460 @@
+typedef struct Dwarf Dwarf;
+typedef struct DwarfAttrs DwarfAttrs;
+typedef struct DwarfBlock DwarfBlock;
+typedef struct DwarfBuf DwarfBuf;
+typedef struct DwarfExpr DwarfExpr;
+typedef struct DwarfSym DwarfSym;
+typedef union DwarfVal DwarfVal;
+
+enum
+{
+	TagArrayType = 0x01,
+	TagClassType = 0x02,
+	TagEntryPoint = 0x03,
+	TagEnumerationType = 0x04,
+	TagFormalParameter = 0x05,
+	TagImportedDeclaration = 0x08,
+	TagLabel = 0x0A,
+	TagLexDwarfBlock = 0x0B,
+	TagMember = 0x0D,
+	TagPointerType = 0x0F,
+	TagReferenceType = 0x10,
+	TagCompileUnit = 0x11,
+	TagStringType = 0x12,
+	TagStructType = 0x13,
+	TagSubroutineType = 0x15,
+	TagTypedef = 0x16,
+	TagUnionType = 0x17,
+	TagUnspecifiedParameters = 0x18,
+	TagVariant = 0x19,
+	TagCommonDwarfBlock = 0x1A,
+	TagCommonInclusion = 0x1B,
+	TagInheritance = 0x1C,
+	TagInlinedSubroutine = 0x1D,
+	TagModule = 0x1E,
+	TagPtrToMemberType = 0x1F,
+	TagSetType = 0x20,
+	TagSubrangeType = 0x21,
+	TagWithStmt = 0x22,
+	TagAccessDeclaration = 0x23,
+	TagBaseType = 0x24,
+	TagCatchDwarfBlock = 0x25,
+	TagConstType = 0x26,
+	TagConstant = 0x27,
+	TagEnumerator = 0x28,
+	TagFileType = 0x29,
+	TagFriend = 0x2A,
+	TagNamelist = 0x2B,
+	TagNamelistItem = 0x2C,
+	TagPackedType = 0x2D,
+	TagSubprogram = 0x2E,
+	TagTemplateTypeParameter = 0x2F,
+	TagTemplateValueParameter = 0x30,
+	TagThrownType = 0x31,
+	TagTryDwarfBlock = 0x32,
+	TagVariantPart = 0x33,
+	TagVariable = 0x34,
+	TagVolatileType = 0x35,
+	TagDwarfProcedure = 0x36,
+	TagRestrictType = 0x37,
+	TagInterfaceType = 0x38,
+	TagNamespace = 0x39,
+	TagImportedModule = 0x3A,
+	TagUnspecifiedType = 0x3B,
+	TagPartialUnit = 0x3C,
+	TagImportedUnit = 0x3D,
+	TagMutableType = 0x3E,
+
+	TypeAddress = 0x01,
+	TypeBoolean = 0x02,
+	TypeComplexFloat = 0x03,
+	TypeFloat = 0x04,
+	TypeSigned = 0x05,
+	TypeSignedChar = 0x06,
+	TypeUnsigned = 0x07,
+	TypeUnsignedChar = 0x08,
+	TypeImaginaryFloat = 0x09,
+
+	AccessPublic = 0x01,
+	AccessProtected = 0x02,
+	AccessPrivate = 0x03,
+
+	VisLocal = 0x01,
+	VisExported = 0x02,
+	VisQualified = 0x03,
+
+	VirtNone = 0x00,
+	VirtVirtual = 0x01,
+	VirtPureVirtual = 0x02,
+
+	LangC89 = 0x0001,
+	LangC = 0x0002,
+	LangAda83 = 0x0003,
+	LangCplusplus = 0x0004,
+	LangCobol74 = 0x0005,
+	LangCobol85 = 0x0006,
+	LangFortran77 = 0x0007,
+	LangFortran90 = 0x0008,
+	LangPascal83 = 0x0009,
+	LangModula2 = 0x000A,
+	LangJava = 0x000B,
+	LangC99 = 0x000C,
+	LangAda95 = 0x000D,
+	LangFortran95 = 0x000E,
+	LangPLI = 0x000F,
+	// 0x8000-0xFFFF reserved
+
+	IdCaseSensitive = 0x00,
+	IdCaseUpper = 0x01,
+	IdCaseLower = 0x02,
+	IdCaseInsensitive = 0x03,
+
+	CallingNormal = 0x01,
+	CallingProgram = 0x02,
+	CallingNocall = 0x03,
+	// 0x40-0xFF reserved
+
+	InNone = 0x00,
+	InInlined = 0x01,
+	InDeclaredNotInlined = 0x02,
+	InDeclaredInlined = 0x03,
+
+	OrderRowMajor = 0x00,
+	OrderColumnMajor = 0x01,
+
+	DiscLabel = 0x00,
+	DiscRange = 0x01,
+
+	TReference = 1<<0,
+	TBlock = 1<<1,
+	TConstant = 1<<2,
+	TString = 1<<3,
+	TFlag = 1<<4,
+	TAddress = 1<<5,
+
+	OpAddr = 0x03,	// 1 op, const addr
+	OpDeref = 0x06,
+	OpConst1u = 0x08,	// 1 op, 1 byte const
+	OpConst1s = 0x09,	//	" signed
+	OpConst2u = 0x0A,	// 1 op, 2 byte const 
+	OpConst2s = 0x0B,	//	" signed
+	OpConst4u = 0x0C,	// 1 op, 4 byte const
+	OpConst4s = 0x0D,	//	" signed
+	OpConst8u = 0x0E,	// 1 op, 8 byte const
+	OpConst8s = 0x0F,	//	" signed
+	OpConstu = 0x10,	// 1 op, LEB128 const
+	OpConsts = 0x11,	//	" signed
+	OpDup = 0x12,
+	OpDrop = 0x13,
+	OpOver = 0x14,
+	OpPick = 0x15,		// 1 op, 1 byte stack index
+	OpSwap = 0x16,
+	OpRot = 0x17,
+	OpXderef = 0x18,
+	OpAbs = 0x19,
+	OpAnd = 0x1A,
+	OpDiv = 0x1B,
+	OpMinus = 0x1C,
+	OpMod = 0x1D,
+	OpMul = 0x1E,
+	OpNeg = 0x1F,
+	OpNot = 0x20,
+	OpOr = 0x21,
+	OpPlus = 0x22,
+	OpPlusUconst = 0x23,	// 1 op, ULEB128 addend
+	OpShl = 0x24,
+	OpShr = 0x25,
+	OpShra = 0x26,
+	OpXor = 0x27,
+	OpSkip = 0x2F,		// 1 op, signed 2-byte constant
+	OpBra = 0x28,		// 1 op, signed 2-byte constant
+	OpEq = 0x29,
+	OpGe = 0x2A,
+	OpGt = 0x2B,
+	OpLe = 0x2C,
+	OpLt = 0x2D,
+	OpNe = 0x2E,
+	OpLit0 = 0x30,
+		// OpLitN = OpLit0 + N for N = 0..31
+	OpReg0 = 0x50,
+		// OpRegN = OpReg0 + N for N = 0..31
+	OpBreg0 = 0x70,	// 1 op, signed LEB128 constant
+		// OpBregN = OpBreg0 + N for N = 0..31
+	OpRegx = 0x90,	// 1 op, ULEB128 register
+	OpFbreg = 0x91,	// 1 op, SLEB128 offset
+	OpBregx = 0x92,	// 2 op, ULEB128 reg, SLEB128 off
+	OpPiece = 0x93,	// 1 op, ULEB128 size of piece
+	OpDerefSize = 0x94,	// 1-byte size of data retrieved
+	OpXderefSize = 0x95,	// 1-byte size of data retrieved
+	OpNop = 0x96,
+	// next four new in Dwarf v3
+	OpPushObjAddr = 0x97,
+	OpCall2 = 0x98,	// 2-byte offset of DIE
+	OpCall4 = 0x99,	// 4-byte offset of DIE
+	OpCallRef = 0x9A,	// 4- or 8- byte offset of DIE
+	// 0xE0-0xFF reserved for user-specific
+};
+
+struct DwarfBlock
+{
+	uchar *data;
+	ulong len;
+};
+
+/* not for consumer use */
+struct DwarfBuf
+{
+	Dwarf *d;
+	uchar *p;
+	uchar *ep;
+	uint addrsize;
+};
+
+union DwarfVal
+{
+	char *s;
+	ulong c;
+	ulong r;
+	DwarfBlock b;
+};
+
+struct DwarfAttrs
+{
+	ulong	tag;
+	uchar	haskids;
+
+	/* whether we have it, along with type */
+	struct {
+		uchar	abstractorigin;
+		uchar	accessibility;
+		uchar	addrclass;
+		uchar	basetypes;
+		uchar	bitoffset;
+		uchar	bitsize;
+		uchar	bytesize;
+		uchar	calling;
+		uchar	commonref;
+		uchar	compdir;
+		uchar	constvalue;
+		uchar	containingtype;
+		uchar	count;
+		uchar	datamemberloc;
+		uchar	declcolumn;
+		uchar	declfile;
+		uchar	declline;
+		uchar	defaultvalue;
+		uchar	discr;
+		uchar	discrlist;
+		uchar	discrvalue;
+		uchar	encoding;
+		uchar	framebase;
+		uchar	friend;
+		uchar	highpc;
+		uchar	identifiercase;
+		uchar	import;
+		uchar	inlined;
+		uchar	isartificial;
+		uchar	isdeclaration;
+		uchar	isexternal;
+		uchar	isoptional;
+		uchar	isprototyped;
+		uchar	isvarparam;
+		uchar	language;
+		uchar	location;
+		uchar	lowerbound;
+		uchar	lowpc;
+		uchar	macroinfo;
+		uchar	name;
+		uchar	namelistitem;
+		uchar	ordering;
+		uchar	priority;
+		uchar	producer;
+		uchar	ranges;
+		uchar	returnaddr;
+		uchar	segment;
+		uchar	sibling;
+		uchar	specification;
+		uchar	startscope;
+		uchar	staticlink;
+		uchar	stmtlist;
+		uchar	stridesize;
+		uchar	stringlength;
+		uchar	type;
+		uchar	upperbound;
+		uchar	uselocation;
+		uchar	virtuality;
+		uchar	visibility;
+		uchar	vtableelemloc;
+	} have;
+
+	ulong	abstractorigin;
+	ulong	accessibility;
+	ulong	addrclass;
+	ulong	basetypes;
+	ulong	bitoffset;
+	ulong	bitsize;
+	ulong	bytesize;
+	ulong	calling;
+	ulong	commonref;
+	char*	compdir;
+	DwarfVal	constvalue;
+	ulong	containingtype;
+	ulong	count;
+	DwarfVal	datamemberloc;
+	ulong	declcolumn;
+	ulong	declfile;
+	ulong	declline;
+	ulong	defaultvalue;
+	ulong	discr;
+	DwarfBlock	discrlist;
+	ulong	discrvalue;
+	ulong	encoding;
+	DwarfVal	framebase;
+	ulong	friend;
+	ulong	highpc;
+	ulong	identifiercase;
+	ulong	import;
+	ulong	inlined;
+	uchar	isartificial;
+	uchar	isdeclaration;
+	uchar	isexternal;
+	uchar	isoptional;
+	uchar	isprototyped;
+	uchar	isvarparam;
+	ulong	language;
+	DwarfVal	location;
+	ulong	lowerbound;
+	ulong	lowpc;
+	ulong	macroinfo;
+	char*	name;
+	DwarfBlock	namelistitem;
+	ulong	ordering;
+	ulong	priority;
+	char*	producer;
+	ulong	ranges;
+	DwarfVal	returnaddr;
+	DwarfVal	segment;
+	ulong	sibling;
+	ulong	specification;
+	ulong	startscope;
+	DwarfVal	staticlink;
+	ulong	stmtlist;
+	ulong	stridesize;
+	DwarfVal	stringlength;
+	ulong	type;
+	ulong	upperbound;
+	DwarfVal	uselocation;
+	ulong	virtuality;
+	ulong	visibility;
+	DwarfVal	vtableelemloc;
+};
+
+enum
+{
+	RuleUndef,
+	RuleSame,
+	RuleCfaOffset,
+	RuleRegister,
+	RuleRegOff,
+	RuleLocation,
+};
+struct DwarfExpr
+{
+	int type;
+	long offset;
+	ulong reg;
+	DwarfBlock loc;
+};
+
+struct DwarfSym
+{
+	DwarfAttrs attrs;
+
+/* not for consumer use... */
+	DwarfBuf b;
+	ulong unit;
+	uint uoff;
+	ulong aoff;
+	int depth;
+	int allunits;
+	ulong nextunit;
+};
+
+
+Dwarf *dwarfopen(Elf *elf);
+void dwarfclose(Dwarf*);
+int dwarfaddrtounit(Dwarf*, ulong, ulong*);
+int dwarflookupfn(Dwarf*, ulong, ulong, DwarfSym*);
+int dwarflookupname(Dwarf*, char*, DwarfSym*);
+int dwarflookupnameinunit(Dwarf*, ulong, char*, DwarfSym*);
+int dwarflookupsubname(Dwarf*, DwarfSym*, char*, DwarfSym*);
+int dwarflookuptag(Dwarf*, ulong, ulong, DwarfSym*);
+int dwarfenumunit(Dwarf*, ulong, DwarfSym*);
+int dwarfseeksym(Dwarf*, ulong, ulong, DwarfSym*);
+int dwarfenum(Dwarf*, DwarfSym*);
+int dwarfnextsym(Dwarf*, DwarfSym*, int);
+int dwarfpctoline(Dwarf*, ulong, char**, char**, char**, ulong*, ulong*, ulong*);
+int dwarfunwind(Dwarf*, ulong, DwarfExpr*, DwarfExpr*, DwarfExpr*, int);
+ulong dwarfget1(DwarfBuf*);
+ulong dwarfget2(DwarfBuf*);
+ulong dwarfget4(DwarfBuf*);
+uvlong dwarfget8(DwarfBuf*);
+ulong dwarfget128(DwarfBuf*);
+long dwarfget128s(DwarfBuf*);
+ulong dwarfgetaddr(DwarfBuf*);
+int dwarfgetn(DwarfBuf*, uchar*, int);
+uchar *dwarfgetnref(DwarfBuf*, ulong);
+char *dwarfgetstring(DwarfBuf*);
+
+
+typedef struct DwarfAbbrev DwarfAbbrev;
+typedef struct DwarfAttr DwarfAttr;
+
+struct DwarfAttr
+{
+	ulong name;
+	ulong form;
+};
+
+struct DwarfAbbrev
+{
+	ulong num;
+	ulong tag;
+	uchar haskids;
+	DwarfAttr *attr;
+	int nattr;
+};
+
+struct Dwarf
+{
+	Elf *elf;
+	int fd;
+	char **reg;
+	int nreg;
+	int addrsize;
+	DwarfBlock abbrev;
+	DwarfBlock aranges;
+	DwarfBlock frame;
+	DwarfBlock info;
+	DwarfBlock line;
+	DwarfBlock pubnames;
+	DwarfBlock pubtypes;
+	DwarfBlock ranges;
+	DwarfBlock str;
+
+	/* little cache */
+	struct {
+		DwarfAbbrev *a;
+		int na;
+		ulong off;
+	} acache;
+};
+
+DwarfAbbrev *dwarfgetabbrev(Dwarf*, ulong, ulong);
+
+int dwarfgetinfounit(Dwarf*, ulong, DwarfBlock*);
+
+extern int dwarf386nregs;
+extern char *dwarf386regs[];
+extern char *dwarf386fp;
+
blob - /dev/null
blob + d5cc9b7113a4b8002fd5c520cd875ff2536ec297 (mode 644)
--- /dev/null
+++ src/libmach/dwarf386.c
@@ -0,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "dwarf.h"
+
+char*
+dwarf386regs[] = 
+{
+	"AX",
+	"CX",
+	"DX",
+	"BX",
+	"SP",
+	"BP",
+	"SI",
+	"DI",
+	"LR",
+	"CFA",
+};
+
+int dwarf386nregs = 10;
+
+
blob - /dev/null
blob + 2db418ddb852364c99e64ed1029145c7ad9e62dc (mode 644)
--- /dev/null
+++ src/libmach/dwarfabbrev.c
@@ -0,0 +1,129 @@
+/*
+ * Dwarf abbreviation parsing code.
+ *
+ * The convention here is that calling dwarfgetabbrevs relinquishes
+ * access to any abbrevs returned previously.  Will have to add 
+ * explicit reference counting if this turns out not to be acceptable.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int parseabbrevs(Dwarf*, ulong, DwarfAbbrev*, DwarfAttr*, int*, int*);
+DwarfAbbrev *dwarfgetabbrev(Dwarf*, ulong, ulong);
+
+static int
+loadabbrevs(Dwarf *d, ulong off, DwarfAbbrev **aa)
+{
+	int nattr, nabbrev;
+	DwarfAbbrev *abbrev;
+	DwarfAttr *attr;
+
+	if(d->acache.off == off && d->acache.na){
+		*aa = d->acache.a;
+		return d->acache.na;
+	}
+
+	/* two passes - once to count, then allocate, then a second to copy */
+	if(parseabbrevs(d, off, nil, nil, &nabbrev, &nattr) < 0)
+		return -1;
+
+	abbrev = malloc(nabbrev*sizeof(DwarfAbbrev) + nattr*sizeof(DwarfAttr));
+	attr = (DwarfAttr*)(abbrev+nabbrev);
+
+	if(parseabbrevs(d, off, abbrev, attr, nil, nil) < 0){
+		free(abbrev);
+		return -1;
+	}
+
+	free(d->acache.a);
+	d->acache.a = abbrev;
+	d->acache.na = nabbrev;
+	d->acache.off = off;
+
+	*aa = abbrev;
+	return nabbrev;
+}
+
+static int
+parseabbrevs(Dwarf *d, ulong off, DwarfAbbrev *abbrev, DwarfAttr *attr, int *pnabbrev, int *pnattr)
+{
+	int i, nabbrev, nattr, haskids;
+	ulong num, tag, name, form;
+	DwarfBuf b;
+
+	if(off >= d->abbrev.len){
+		werrstr("bad abbrev section offset 0x%lux >= 0x%lux\n", off, d->abbrev.len);
+		return -1;
+	}
+
+	memset(&b, 0, sizeof b);
+	b.p = d->abbrev.data + off;
+	b.ep = d->abbrev.data + d->abbrev.len;
+
+	nabbrev = 0;
+	nattr = 0;
+	for(;;){
+		if(b.p == nil){
+			werrstr("malformed abbrev data");
+			return -1;
+		}
+		num = dwarfget128(&b);
+		if(num == 0)
+			break;
+		tag = dwarfget128(&b);
+		haskids = dwarfget1(&b);
+		for(i=0;; i++){
+			name = dwarfget128(&b);
+			form = dwarfget128(&b);
+			if(name == 0 && form == 0)
+				break;
+			if(attr){
+				attr[i].name = name;
+				attr[i].form = form;
+			}
+		}
+		if(abbrev){
+			abbrev->num = num;
+			abbrev->tag = tag;
+			abbrev->haskids = haskids;
+			abbrev->attr = attr;
+			abbrev->nattr = i;
+			abbrev++;
+			attr += i;
+		}
+		nabbrev++;
+		nattr += i;
+	}
+	if(pnabbrev)
+		*pnabbrev = nabbrev;
+	if(pnattr)
+		*pnattr = nattr;
+	return 0;
+}
+
+static DwarfAbbrev*
+findabbrev(DwarfAbbrev *a, int na, ulong num)
+{
+	int i;
+
+	for(i=0; i<na; i++)
+		if(a[i].num == num)
+			return &a[i];
+	return nil;
+}
+
+DwarfAbbrev*
+dwarfgetabbrev(Dwarf *d, ulong off, ulong num)
+{
+	DwarfAbbrev *a;
+	int na;
+
+	if((na = loadabbrevs(d, off, &a)) < 0)
+		return nil;
+	return findabbrev(a, na, num);
+}
+
blob - /dev/null
blob + 212995881c2e6a6ce4bb31d54080c079ca0b7283 (mode 644)
--- /dev/null
+++ src/libmach/dwarfaranges.c
@@ -0,0 +1,63 @@
+/*
+ * Dwarf address ranges parsing code.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+int
+dwarfaddrtounit(Dwarf *d, ulong addr, ulong *unit)
+{
+	DwarfBuf b;
+	int segsize, i;
+	ulong len, id, off, base, size;
+	uchar *start, *end;
+
+	memset(&b, 0, sizeof b);
+	b.d = d;
+	b.p = d->aranges.data;
+	b.ep = b.p + d->aranges.len;
+
+	while(b.p < b.ep){
+		start = b.p;
+		len = dwarfget4(&b);
+		if((id = dwarfget2(&b)) != 2){
+			if(b.p == nil){
+			underflow:
+				werrstr("buffer underflow reading address ranges header");
+			}else
+				werrstr("bad dwarf version 0x%lux in address ranges header", id);
+			return -1;
+		}
+		off = dwarfget4(&b);
+		b.addrsize = dwarfget1(&b);
+		if(d->addrsize == 0)
+			d->addrsize = b.addrsize;
+		segsize = dwarfget1(&b);
+		USED(segsize);	/* what am i supposed to do with this? */
+		if(b.p == nil)
+			goto underflow;
+		if((i = (b.p-start) % (2*b.addrsize)) != 0)
+			b.p += 2*b.addrsize - i;
+		end = start+4+len;
+		while(b.p!=nil && b.p<end){
+			base = dwarfgetaddr(&b);
+			size = dwarfgetaddr(&b);
+			if(b.p == nil)
+				goto underflow;
+			if(base <= addr && addr < base+size){
+				*unit = off;
+				return 0;
+			}
+		}
+		if(b.p == nil)
+			goto underflow;
+		b.p = end;
+	}
+	werrstr("address 0x%lux is not listed in dwarf debugging symbols", addr);
+	return -1;
+}
+
blob - /dev/null
blob + de342cd7046fa7e1e038b53504538097415e0b73 (mode 644)
--- /dev/null
+++ src/libmach/dwarfcfa.c
@@ -0,0 +1,391 @@
+/*
+ * Dwarf call frame unwinding.
+ *
+ * The call frame unwinding values are encoded using a state machine 
+ * like the pc<->line mapping, but it's a different machine.  
+ * The expressions to generate the old values are similar in function to the 
+ * ``dwarf expressions'' used for locations in the code, but of course not
+ * the same encoding.  
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+#define trace 0
+
+typedef struct State State;
+struct State
+{
+	ulong loc;
+	ulong endloc;
+	ulong iquantum;
+	ulong dquantum;
+	char *augmentation;
+	int version;
+	ulong rareg;
+	DwarfBuf init;
+	DwarfExpr *cfa;
+	DwarfExpr *ra;
+	DwarfExpr *r;
+	DwarfExpr *initr;
+	int nr;
+	DwarfExpr **stack;
+	int nstack;
+};
+
+static int findfde(Dwarf*, ulong, State*, DwarfBuf*);
+static int dexec(DwarfBuf*, State*, int);
+
+int
+dwarfunwind(Dwarf *d, ulong pc, DwarfExpr *cfa, DwarfExpr *ra, DwarfExpr *r, int nr)
+{
+	int i, ret;
+	DwarfBuf fde, b;
+	DwarfExpr *initr;
+	State s;
+
+	initr = mallocz(nr*sizeof(initr[0]), 1);
+	if(initr == 0)
+		return -1;
+
+	memset(&s, 0, sizeof s);
+	s.loc = 0;
+	s.cfa = cfa;
+	s.ra = ra;
+	s.r = r;
+	s.nr = nr;
+
+	if(findfde(d, pc, &s, &fde) < 0){
+		free(initr);
+		return -1;
+	}
+
+	memset(r, 0, nr*sizeof(r[0]));
+	for(i=0; i<nr; i++)
+		r[i].type = RuleSame;
+	if(trace) fprint(2, "s.init %p-%p, fde %p-%p\n", s.init.p, s.init.ep, fde.p, fde.ep);
+	b = s.init;
+	if(dexec(&b, &s, 0) < 0)
+		goto err;
+
+	s.initr = initr;
+	memmove(initr, r, nr*sizeof(initr[0]));
+
+	if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
+	while(s.loc < pc){
+		if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
+		if(dexec(&fde, &s, 1) < 0)
+			goto err;
+	}
+	*ra = s.r[s.rareg];
+
+	ret = 0;
+	goto out;
+
+err:
+	ret = -1;
+out:
+	free(initr);
+	for(i=0; i<s.nstack; i++)
+		free(s.stack[i]);
+	free(s.stack);
+	return ret;
+}
+
+/*
+ * XXX This turns out to be much more expensive than the actual
+ * running of the machine in dexec.  It probably makes sense to
+ * cache the last 10 or so fde's we've found, since stack traces 
+ * will keep asking for the same info over and over.
+ */
+static int
+findfde(Dwarf *d, ulong pc, State *s, DwarfBuf *fde)
+{
+	static int nbad;
+	char *aug;
+	uchar *next;
+	int i, vers;
+	ulong len, id, base, size;
+	DwarfBuf b;
+
+	b.d = d;
+	b.p = d->frame.data;
+	b.ep = b.p + d->frame.len;
+	b.addrsize = d->addrsize;
+	if(b.addrsize == 0)
+		b.addrsize = 4;	/* where should i find this? */
+
+	for(; b.p < b.ep; b.p = next){
+		if((i = (b.p - d->frame.data) % b.addrsize))
+			b.p += b.addrsize - i;
+		len = dwarfget4(&b);
+		if(len > b.ep-b.p){
+			werrstr("bad length in cie/fde header");
+			return -1;
+		}
+		next = b.p+len;
+		id = dwarfget4(&b);
+		if(id == 0xFFFFFFFF){	/* CIE */
+			vers = dwarfget1(&b);
+			if(vers != 1 && vers != 2 && vers != 3){
+				if(++nbad == 1)
+					fprint(2, "unknown cie version %d (wanted 1-3)\n", vers);
+				continue;
+			}
+			aug = dwarfgetstring(&b);
+			if(aug && *aug){
+				if(++nbad == 1)
+					fprint(2, "unknown augmentation: %s\n", aug);
+				continue;
+			}
+			s->iquantum = dwarfget128(&b);
+			s->dquantum = dwarfget128s(&b);
+			s->rareg = dwarfget128(&b);
+			if(s->rareg > s->nr){
+				werrstr("return address is register %d but only have %d registers",
+					s->rareg, s->nr);
+				return -1;
+			}
+			s->init.p = b.p;
+			s->init.ep = next;
+		}else{	/* FDE */
+			base = dwarfgetaddr(&b);
+			size = dwarfgetaddr(&b);
+			fde->p = b.p;
+			fde->ep = next;
+			s->loc = base;
+			s->endloc = base+size;
+			if(base <= pc && pc < base+size)
+				return 0;
+		}
+	}
+	werrstr("cannot find call frame information for pc 0x%lux", pc);
+	return -1;
+			
+}
+
+static int
+checkreg(State *s, long r)
+{
+	if(r < 0 || r >= s->nr){
+		werrstr("bad register number 0x%lux", r);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+dexec(DwarfBuf *b, State *s, int locstop)
+{
+	int c;
+	long arg1, arg2;
+	DwarfExpr *e, **p;
+
+	for(;;){
+		if(b->p == b->ep){
+			if(s->initr)
+				s->loc = s->endloc;
+			return 0;
+		}
+		c = dwarfget1(b);
+		if(b->p == nil){
+			werrstr("ran out of instructions during cfa program");
+			if(trace) fprint(2, "%r\n");
+			return -1;
+		}
+		if(trace) fprint(2, "+ loc=0x%lux op 0x%ux ", s->loc, c);
+		switch(c>>6){
+		case 1:	/* advance location */
+			arg1 = c&0x3F;
+		advance:
+			if(trace) fprint(2, "loc += %ld\n", arg1*s->iquantum);
+			s->loc += arg1 * s->iquantum;
+			if(locstop)
+				return 0;
+			continue;
+		
+		case 2:	/* offset rule */
+			arg1 = c&0x3F;
+			arg2 = dwarfget128(b);
+		offset:
+			if(trace) fprint(2, "r%ld += %ld\n", arg1, arg2*s->dquantum);
+			if(checkreg(s, arg1) < 0)
+				return -1;
+			s->r[arg1].type = RuleCfaOffset;
+			s->r[arg1].offset = arg2 * s->dquantum;
+			continue;
+
+		case 3:	/* restore initial setting */
+			arg1 = c&0x3F;
+		restore:
+			if(trace) fprint(2, "r%ld = init\n", arg1);
+			if(checkreg(s, arg1) < 0)
+				return -1;
+			s->r[arg1] = s->initr[arg1];
+			continue;
+		}
+
+		switch(c){
+		case 0:	/* nop */
+			if(trace) fprint(2, "nop\n");
+			continue;
+
+		case 0x01:	/* set location */
+			s->loc = dwarfgetaddr(b);
+			if(trace) fprint(2, "loc = 0x%lux\n", s->loc);
+			if(locstop)
+				return 0;
+			continue;
+
+		case 0x02:	/* advance loc1 */
+			arg1 = dwarfget1(b);
+			goto advance;
+
+		case 0x03:	/* advance loc2 */
+			arg1 = dwarfget2(b);
+			goto advance;
+
+		case 0x04:	/* advance loc4 */
+			arg1 = dwarfget4(b);
+			goto advance;
+
+		case 0x05:	/* offset extended */
+			arg1 = dwarfget128(b);
+			arg2 = dwarfget128(b);
+			goto offset;
+
+		case 0x06:	/* restore extended */
+			arg1 = dwarfget128(b);
+			goto restore;
+
+		case 0x07:	/* undefined */
+			arg1 = dwarfget128(b);
+			if(trace) fprint(2, "r%ld = undef\n", arg1);
+			if(checkreg(s, arg1) < 0)
+				return -1;
+			s->r[arg1].type = RuleUndef;
+			continue;
+
+		case 0x08:	/* same value */
+			arg1 = dwarfget128(b);
+			if(trace) fprint(2, "r%ld = same\n", arg1);
+			if(checkreg(s, arg1) < 0)
+				return -1;
+			s->r[arg1].type = RuleSame;
+			continue;
+
+		case 0x09:	/* register */
+			arg1 = dwarfget128(b);
+			arg2 = dwarfget128(b);
+			if(trace) fprint(2, "r%ld = r%ld\n", arg1, arg2);
+			if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0)
+				return -1;
+			s->r[arg1].type = RuleRegister;
+			s->r[arg1].reg = arg2;
+			continue;
+
+		case 0x0A:	/* remember state */
+			e = malloc(s->nr*sizeof(e[0]));
+			if(trace) fprint(2, "push\n");
+			if(e == nil)
+				return -1;
+			p = realloc(s->stack, (s->nstack+1)*sizeof(s->stack[0]));
+			if(p == nil){
+				free(e);
+				return -1;
+			}
+			s->stack[s->nstack++] = e;
+			memmove(e, s->r, s->nr*sizeof(e[0]));
+			continue;
+
+		case 0x0B:	/* restore state */
+			if(trace) fprint(2, "pop\n");
+			if(s->nstack == 0){
+				werrstr("restore state underflow");
+				return -1;
+			}
+			e = s->stack[s->nstack-1];
+			memmove(s->r, e, s->nr*sizeof(e[0]));
+			p = realloc(s->stack, (s->nstack-1)*sizeof(s->stack[0]));
+			if(p == nil)
+				return -1;
+			free(e);
+			s->nstack--;
+			continue;
+
+		case 0x0C:	/* def cfa */
+			arg1 = dwarfget128(b);
+			arg2 = dwarfget128(b);
+		defcfa:
+			if(trace) fprint(2, "cfa %ld(r%ld)\n", arg2, arg1);
+			if(checkreg(s, arg1) < 0)
+				return -1;
+			s->cfa->type = RuleRegOff;
+			s->cfa->reg = arg1;
+			s->cfa->offset = arg2;
+			continue;
+
+		case 0x0D:	/* def cfa register */	
+			arg1 = dwarfget128(b);
+			if(trace) fprint(2, "cfa reg r%ld\n", arg1);
+			if(s->cfa->type != RuleRegOff){
+				werrstr("change CFA register but CFA not in register+offset form");
+				return -1;	
+			}
+			if(checkreg(s, arg1) < 0)
+				return -1;
+			s->cfa->reg = arg1;
+			continue;
+
+		case 0x0E:	/* def cfa offset */
+			arg1 = dwarfget128(b);
+		cfaoffset:
+			if(trace) fprint(2, "cfa off %ld\n", arg1);
+			if(s->cfa->type != RuleRegOff){
+				werrstr("change CFA offset but CFA not in register+offset form");
+				return -1;
+			}
+			s->cfa->offset = arg1;
+			continue;
+
+		case 0x0F:	/* def cfa expression */
+			if(trace) fprint(2, "cfa expr\n");
+			s->cfa->type = RuleLocation;
+			s->cfa->loc.len = dwarfget128(b);
+			s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len);
+			continue;
+
+		case 0x10:	/* def reg expression */
+			arg1 = dwarfget128(b);
+			if(trace) fprint(2, "reg expr r%ld\n", arg1);
+			if(checkreg(s, arg1) < 0)
+				return -1;
+			s->r[arg1].type = RuleLocation;
+			s->r[arg1].loc.len = dwarfget128(b);
+			s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len);
+			continue;
+
+		case 0x11:	/* offset extended */
+			arg1 = dwarfget128(b);
+			arg2 = dwarfget128s(b);
+			goto offset;
+
+		case 0x12:	/* cfa sf */
+			arg1 = dwarfget128(b);
+			arg2 = dwarfget128s(b);
+			goto defcfa;
+
+		case 0x13:	/* cfa offset sf */
+			arg1 = dwarfget128s(b);
+			goto cfaoffset;
+
+		default:	/* unknown */
+			werrstr("unknown opcode 0x%ux in cfa program", c);
+			return -1;
+		}
+	}
+	return -1;		/* not reached */		
+}
+
blob - /dev/null
blob + 47c38c378c6239726c223bb2905785f49320440e (mode 644)
--- /dev/null
+++ src/libmach/dwarfdump.c
@@ -0,0 +1,138 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+void printrules(Dwarf *d, ulong pc);
+int exprfmt(Fmt*);
+
+void
+usage(void)
+{
+	fprint(2, "usage: dwarfdump file\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int c;
+	Elf *elf;
+	Dwarf *d;
+	DwarfSym s;
+	char *cdir, *dir, *file;
+	ulong line, mtime, length;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	if(argc != 1)
+		usage();
+
+	fmtinstall('R', exprfmt);
+	fmtinstall('H', encodefmt);
+
+	if((elf = elfopen(argv[0])) == nil)
+		sysfatal("elfopen %s: %r", argv[0]);
+	if((d=dwarfopen(elf)) == nil)
+		sysfatal("dwarfopen: %r");
+
+	if(dwarfenum(d, &s) < 0)
+		sysfatal("dwarfenumall: %r");
+
+	while(dwarfnextsym(d, &s, 1) == 1){
+		switch(s.attrs.tag){
+		case TagCompileUnit:
+			print("compileunit %s\n", s.attrs.name);
+			break;
+		case TagSubprogram:
+			c = 't';
+			goto sym;
+		case TagVariable:
+			c = 'd';
+			goto sym;
+		case TagConstant:
+			c = 'c';
+			goto sym;
+		case TagFormalParameter:
+			if(!s.attrs.name)
+				break;
+			c = 'p';
+		sym:
+			if(s.attrs.isexternal)
+				c += 'A' - 'a';
+			print("%c %s", c, s.attrs.name);
+			if(s.attrs.have.lowpc)
+				print(" 0x%lux-0x%lux", s.attrs.lowpc, s.attrs.highpc);
+			switch(s.attrs.have.location){
+			case TBlock:
+				print(" @ %.*H", s.attrs.location.b.len, s.attrs.location.b.data);
+				break;
+			case TConstant:
+				print(" @ 0x%lux", s.attrs.location.c);
+				break;
+			}
+			if(s.attrs.have.ranges)
+				print(" ranges@0x%lux", s.attrs.ranges);
+			print("\n");
+			if(s.attrs.have.lowpc){
+				if(dwarfpctoline(d, s.attrs.lowpc, &cdir, &dir, &file, &line, &mtime, &length) < 0)
+					print("\tcould not find source: %r\n");
+				else
+					print("\t%s/%s/%s:%lud mtime=%lud length=%lud\n",
+						cdir, dir, file, line, mtime, length);
+
+				if(0) printrules(d, s.attrs.lowpc);
+				if(0) printrules(d, (s.attrs.lowpc+s.attrs.highpc)/2);
+			}
+			break;
+		}
+	}
+	exits(0);
+}
+
+void
+printrules(Dwarf *d, ulong pc)
+{
+	int i;
+	DwarfExpr r[10];
+	DwarfExpr cfa, ra;
+
+	if(dwarfunwind(d, pc, &cfa, &ra, r, nelem(r)) < 0)
+		print("\tcannot unwind from pc 0x%lux: %r\n", pc);
+
+	print("\tpc=0x%lux cfa=%R ra=%R", pc, &cfa, &ra);
+	for(i=0; i<nelem(r); i++)
+		if(r[i].type != RuleSame)
+			print(" r%d=%R", i, &r[i]);
+	print("\n");
+}
+
+int
+exprfmt(Fmt *fmt)
+{
+	DwarfExpr *e;
+
+	if((e = va_arg(fmt->args, DwarfExpr*)) == nil)
+		return fmtstrcpy(fmt, "<nil>");
+
+	switch(e->type){
+	case RuleUndef:
+		return fmtstrcpy(fmt, "undef");
+	case RuleSame:
+		return fmtstrcpy(fmt, "same");
+	case RuleCfaOffset:
+		return fmtprint(fmt, "%ld(cfa)", e->offset);
+	case RuleRegister:
+		return fmtprint(fmt, "r%ld", e->reg);
+	case RuleRegOff:
+		return fmtprint(fmt, "%ld(r%ld)", e->offset, e->reg);
+	case RuleLocation:
+		return fmtprint(fmt, "l.%.*H", e->loc.len, e->loc.data);
+	default:
+		return fmtprint(fmt, "?%d", e->type);
+	}
+}
blob - /dev/null
blob + 0da7273a11fbd6c35a593872c6e060e6dfb935c2 (mode 644)
--- /dev/null
+++ src/libmach/dwarfeval.c
@@ -0,0 +1,62 @@
+	OpAddr = 0x03,	// 1 op, const addr
+	OpDeref = 0x06,
+	OpConst1u = 0x08,	// 1 op, 1 byte const
+	OpConst1s = 0x09,	//	" signed
+	OpConst2u = 0x0A	// 1 op, 2 byte const 
+	OpConst2s = 0x0B,	//	" signed
+	OpConst4u = 0x0C,	// 1 op, 4 byte const
+	OpConst4s = 0x0D,	//	" signed
+	OpConst8u = 0x0E,	// 1 op, 8 byte const
+	OpConst8s = 0x0F,	//	" signed
+	OpConstu = 0x10,	// 1 op, LEB128 const
+	OpConsts = 0x11,	//	" signed
+	OpDup = 0x12,
+	OpDrop = 0x13,
+	OpOver = 0x14,
+	OpPick = 0x15,		// 1 op, 1 byte stack index
+	OpSwap = 0x16,
+	OpRot = 0x17,
+	OpXderef = 0x18,
+	OpAbs = 0x19,
+	OpAnd = 0x1A,
+	OpDiv = 0x1B,
+	OpMinus = 0x1C,
+	OpMod = 0x1D,
+	OpMul = 0x1E,
+	OpNeg = 0x1F,
+	OpNot = 0x20,
+	OpOr = 0x21,
+	OpPlus = 0x22,
+	OpPlusUconst = 0x23,	// 1 op, ULEB128 addend
+	OpShl = 0x24,
+	OpShr = 0x25,
+	OpShra = 0x26,
+	OpXor = 0x27,
+	OpSkip = 0x2F,		// 1 op, signed 2-byte constant
+	OpBra = 0x28,		// 1 op, signed 2-byte constant
+	OpEq = 0x29,
+	OpGe = 0x2A,
+	OpGt = 0x2B,
+	OpLe = 0x2C,
+	OpLt = 0x2D,
+	OpNe = 0x2E,
+	OpLit0 = 0x30,
+		// OpLitN = OpLit0 + N for N = 0..31
+	OpReg0 = 0x50,
+		// OpRegN = OpReg0 + N for N = 0..31
+	OpBreg0 = 0x70,	// 1 op, signed LEB128 constant
+		// OpBregN = OpBreg0 + N for N = 0..31
+	OpRegx = 0x90,	// 1 op, ULEB128 register
+	OpFbreg = 0x91,	// 1 op, SLEB128 offset
+	OpBregx = 0x92,	// 2 op, ULEB128 reg, SLEB128 off
+	OpPiece = 0x93,	// 1 op, ULEB128 size of piece
+	OpDerefSize = 0x94,	// 1-byte size of data retrieved
+	OpXderefSize = 0x95,	// 1-byte size of data retrieved
+	OpNop = 0x96,
+	// next four new in Dwarf v3
+	OpPushObjAddr = 0x97,
+	OpCall2 = 0x98,	// 2-byte offset of DIE
+	OpCall4 = 0x99,	// 4-byte offset of DIE
+	OpCallRef = 0x9A,	// 4- or 8- byte offset of DIE
+	// 0xE0-0xFF reserved for user-specific
+
blob - /dev/null
blob + da8558c4b741376f9f6cffc348ab266275e7f13b (mode 644)
--- /dev/null
+++ src/libmach/dwarfget.c
@@ -0,0 +1,217 @@
+/*
+ * Dwarf data format parsing routines.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+ulong
+dwarfget1(DwarfBuf *b)
+{
+	if(b->p==nil || b->p+1 > b->ep){
+		b->p = nil;
+		return 0;
+	}
+	return *b->p++;
+}
+
+int
+dwarfgetn(DwarfBuf *b, uchar *a, int n)
+{
+	if(b->p==nil || b->p+n > b->ep){
+		b->p = nil;
+		memset(a, 0, n);
+		return -1;
+	}
+	memmove(a, b->p, n);
+	b->p += n;
+	return 0;
+}
+
+uchar*
+dwarfgetnref(DwarfBuf *b, ulong n)
+{
+	uchar *p;
+
+	if(b->p==nil || b->p+n > b->ep){
+		b->p = nil;
+		return nil;
+	}
+	p = b->p;
+	b->p += n;
+	return p;
+}
+
+char*
+dwarfgetstring(DwarfBuf *b)
+{
+	char *s;
+
+	if(b->p == nil)
+		return nil;
+	s = (char*)b->p;
+	while(b->p < b->ep && *b->p)
+		b->p++;
+	if(b->p >= b->ep){
+		b->p = nil;
+		return nil;
+	}
+	b->p++;
+	return s;
+}
+
+void
+dwarfskip(DwarfBuf *b, int n)
+{
+	if(b->p==nil || b->p+n > b->ep)
+		b->p = nil;
+	else
+		b->p += n;
+}
+
+ulong
+dwarfget2(DwarfBuf *b)
+{
+	ulong v;
+
+	if(b->p==nil || b->p+2 > b->ep){
+		b->p = nil;
+		return 0;
+	}
+	v = b->d->elf->hdr.e2(b->p);
+	b->p += 2;
+	return v;
+}
+
+ulong
+dwarfget4(DwarfBuf *b)
+{
+	ulong v;
+
+	if(b->p==nil || b->p+4 > b->ep){
+		b->p = nil;
+		return 0;
+	}
+	v = b->d->elf->hdr.e4(b->p);
+	b->p += 4;
+	return v;
+}
+
+uvlong
+dwarfget8(DwarfBuf *b)
+{
+	uvlong v;
+
+	if(b->p==nil || b->p+8 > b->ep){
+		b->p = nil;
+		return 0;
+	}
+	v = b->d->elf->hdr.e8(b->p);
+	b->p += 8;
+	return v;
+}
+
+ulong
+dwarfgetaddr(DwarfBuf *b)
+{
+	static int nbad;
+
+	if(b->addrsize == 0)
+		b->addrsize = b->d->addrsize;
+
+	switch(b->addrsize){
+	case 1:
+		return dwarfget1(b);
+	case 2:
+		return dwarfget2(b);
+	case 4:
+		return dwarfget4(b);
+	case 8:
+		return dwarfget8(b);
+	default:
+		if(++nbad == 1)
+			fprint(2, "dwarf: unexpected address size %lud in dwarfgetaddr\n", b->addrsize);
+		b->p = nil;
+		return 0;
+	}
+}
+
+int n1, n2, n3, n4, n5;
+
+/* An inline function picks off the calls to dwarfget128 for 1-byte encodings,
+ * more than by far the common case (99.999% on most binaries!). */
+ulong
+dwarfget128(DwarfBuf *b)
+{
+	static int nbad;
+	ulong c, d;
+
+	if(b->p == nil)
+		return 0;
+	c = *b->p++;
+	if(!(c&0x80))
+{n1++;
+		return c;
+}
+	d = *b->p++;
+	c |= (d&0x7F)<<7;
+	if(!(d&0x80))
+{n2++;
+		return c;
+}
+	d = *b->p++;
+	c |= (d&0x7F)<<14;
+	if(!(d&0x80))
+{n3++;
+		return c;
+}
+	d = *b->p++;
+	c |= (d&0x7F)<<21;
+	if(!(d&0x80))
+{n4++;
+		return c;
+}
+	d = *b->p++;
+	c |= (d&0x7F)<<28;
+	if(!(d&0x80))
+{n5++;
+		return c;
+}
+	while(b->p<b->ep && *b->p&0x80)
+		b->p++;
+	if(++nbad == 1)
+		fprint(2, "dwarf: overflow during parsing of uleb128 integer\n");
+	return c;
+}
+
+long
+dwarfget128s(DwarfBuf *b)
+{
+	int nb, c;
+	ulong v;
+	static int nbad;
+
+	v = 0;
+	nb = 0;
+	if(b->p==nil)
+		return 0;
+	while(b->p<b->ep){
+		c = *b->p++;
+		v |= (c & 0x7F)<<nb;
+		nb += 7;
+		if(!(c&0x80))
+			break;
+	}
+	if(v&(1<<(nb-1)))
+		v |= ~(((ulong)1<<nb)-1);
+	if(nb > 8*sizeof(ulong)){
+		if(0)
+		if(++nbad == 1)
+			fprint(2, "dwarf: overflow during parsing of sleb128 integer: got %d bits\n", nb);
+	}
+	return v;
+}
+
blob - /dev/null
blob + 79065bba7e80b7f52504e4c4076d58aa59e471f9 (mode 644)
--- /dev/null
+++ src/libmach/dwarfinfo.c
@@ -0,0 +1,646 @@
+/*
+ * Dwarf info parse and search.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+enum
+{
+	DwarfAttrSibling = 0x01,
+	DwarfAttrLocation = 0x02,
+	DwarfAttrName = 0x03,
+	DwarfAttrOrdering = 0x09,
+	DwarfAttrByteSize = 0x0B,
+	DwarfAttrBitOffset = 0x0C,
+	DwarfAttrBitSize = 0x0D,
+	DwarfAttrStmtList = 0x10,
+	DwarfAttrLowpc = 0x11,
+	DwarfAttrHighpc = 0x12,
+	DwarfAttrLanguage = 0x13,
+	DwarfAttrDiscr = 0x15,
+	DwarfAttrDiscrValue = 0x16,
+	DwarfAttrVisibility = 0x17,
+	DwarfAttrImport = 0x18,
+	DwarfAttrStringLength = 0x19,
+	DwarfAttrCommonRef = 0x1A,
+	DwarfAttrCompDir = 0x1B,
+	DwarfAttrConstValue = 0x1C,
+	DwarfAttrContainingType = 0x1D,
+	DwarfAttrDefaultValue = 0x1E,
+	DwarfAttrInline = 0x20,
+	DwarfAttrIsOptional = 0x21,
+	DwarfAttrLowerBound = 0x22,
+	DwarfAttrProducer = 0x25,
+	DwarfAttrPrototyped = 0x27,
+	DwarfAttrReturnAddr = 0x2A,
+	DwarfAttrStartScope = 0x2C,
+	DwarfAttrStrideSize = 0x2E,
+	DwarfAttrUpperBound = 0x2F,
+	DwarfAttrAbstractOrigin = 0x31,
+	DwarfAttrAccessibility = 0x32,
+	DwarfAttrAddrClass = 0x33,
+	DwarfAttrArtificial = 0x34,
+	DwarfAttrBaseTypes = 0x35,
+	DwarfAttrCalling = 0x36,
+	DwarfAttrCount = 0x37,
+	DwarfAttrDataMemberLoc = 0x38,
+	DwarfAttrDeclColumn = 0x39,
+	DwarfAttrDeclFile = 0x3A,
+	DwarfAttrDeclLine = 0x3B,
+	DwarfAttrDeclaration = 0x3C,
+	DwarfAttrDiscrList = 0x3D,
+	DwarfAttrEncoding = 0x3E,
+	DwarfAttrExternal = 0x3F,
+	DwarfAttrFrameBase = 0x40,
+	DwarfAttrFriend = 0x41,
+	DwarfAttrIdentifierCase = 0x42,
+	DwarfAttrMacroInfo = 0x43,
+	DwarfAttrNamelistItem = 0x44,
+	DwarfAttrPriority = 0x45,
+	DwarfAttrSegment = 0x46,
+	DwarfAttrSpecification = 0x47,
+	DwarfAttrStaticLink = 0x48,
+	DwarfAttrType = 0x49,
+	DwarfAttrUseLocation = 0x4A,
+	DwarfAttrVarParam = 0x4B,
+	DwarfAttrVirtuality = 0x4C,
+	DwarfAttrVtableElemLoc = 0x4D,
+	DwarfAttrAllocated = 0x4E,
+	DwarfAttrAssociated = 0x4F,
+	DwarfAttrDataLocation = 0x50,
+	DwarfAttrStride = 0x51,
+	DwarfAttrEntrypc = 0x52,
+	DwarfAttrUseUTF8 = 0x53,
+	DwarfAttrExtension = 0x54,
+	DwarfAttrRanges = 0x55,
+	DwarfAttrTrampoline = 0x56,
+	DwarfAttrCallColumn = 0x57,
+	DwarfAttrCallFile = 0x58,
+	DwarfAttrCallLine = 0x59,
+	DwarfAttrDescription = 0x5A,
+	DwarfAttrMax,
+
+	FormAddr = 0x01,
+	FormDwarfBlock2 = 0x03,
+	FormDwarfBlock4 = 0x04,
+	FormData2 = 0x05,
+	FormData4 = 0x06,
+	FormData8 = 0x07,
+	FormString = 0x08,
+	FormDwarfBlock = 0x09,
+	FormDwarfBlock1 = 0x0A,
+	FormData1 = 0x0B,
+	FormFlag = 0x0C,
+	FormSdata = 0x0D,
+	FormStrp = 0x0E,
+	FormUdata = 0x0F,
+	FormRefAddr = 0x10,
+	FormRef1 = 0x11,
+	FormRef2 = 0x12,
+	FormRef4 = 0x13,
+	FormRef8 = 0x14,
+	FormRefUdata = 0x15,
+	FormIndirect = 0x16,
+};
+
+static int parseattrs(DwarfBuf*, ulong, DwarfAbbrev*, DwarfAttrs*);
+static int getulong(DwarfBuf*, int, ulong, ulong*, int*);
+static int getuchar(DwarfBuf*, int, uchar*);
+static int getstring(DwarfBuf*, int, char**);
+static int getblock(DwarfBuf*, int, DwarfBlock*);
+static int skipform(DwarfBuf*, int);
+static int constblock(Dwarf*, DwarfBlock*, ulong*);
+
+int
+dwarflookupnameinunit(Dwarf *d, ulong unit, char *name, DwarfSym *s)
+{
+	if(dwarfenumunit(d, unit, s) < 0)
+		return -1;
+
+	dwarfnextsym(d, s, 1);	/* s is now the CompileUnit */
+	if(dwarfnextsym(d, s, 1) == 1){	/* s is now the first child of the compile unit */
+		do{
+			if(s->attrs.name && strcmp(s->attrs.name, name) == 0)
+				return 0;
+		}while(dwarfnextsym(d, s, 0) == 1);
+	} 
+	werrstr("symbol '%s' not found", name);
+	return -1;
+}
+	
+
+int
+dwarflookupsubname(Dwarf *d, DwarfSym *parent, char *name, DwarfSym *s)
+{
+	*s = *parent;
+	dwarfnextsym(d, s, 1);
+	if(s->depth == parent->depth+1)
+		do{
+			if(s->attrs.name && strcmp(s->attrs.name, name) == 0)
+				return 0;
+		}while(dwarfnextsym(d, s, 0) == 1);
+	werrstr("symbol '%s' not found", name);
+	return -1;
+}
+
+int
+dwarflookuptag(Dwarf *d, ulong unit, ulong tag, DwarfSym *s)
+{
+	if(dwarfenumunit(d, unit, s) < 0)
+		return -1;
+
+	dwarfnextsym(d, s, 1);	/* s is now the CompileUnit */
+	if(s->attrs.tag == tag)
+		return 0;
+
+	if(dwarfnextsym(d, s, 1) == 1){	/* s is now the first child of the compile unit */
+		do{
+			if(s->attrs.tag == tag)
+				return 0;
+		}while(dwarfnextsym(d, s, 0) == 1);
+	} 
+	werrstr("symbol with tag 0x%lux not found", tag);
+	return -1;
+}
+
+int
+dwarfseeksym(Dwarf *d, ulong unit, ulong off, DwarfSym *s)
+{
+	if(dwarfenumunit(d, unit, s) < 0)
+		return -1;
+	s->b.p = d->info.data + unit + off;
+	if(dwarfnextsym(d, s, 1) != 1)
+		return -1;
+	return 0;
+}
+
+int
+dwarflookupfn(Dwarf *d, ulong unit, ulong pc, DwarfSym *s)
+{
+	if(dwarfenumunit(d, unit, s) < 0)
+		return -1;
+
+	if(dwarfnextsym(d, s, 1) != 1)
+		return -1;
+	/* s is now the CompileUnit */
+
+	if(dwarfnextsym(d, s, 1) == 1){	/* s is now the first child of the compile unit */
+		do{
+			if(s->attrs.tag != TagSubprogram)
+				continue;
+			if(s->attrs.lowpc <= pc && pc < s->attrs.highpc)
+				return 0;
+		}while(dwarfnextsym(d, s, 0) == 1);
+	} 
+	werrstr("fn containing pc 0x%lux not found", pc);
+	return -1;
+}
+
+int
+dwarfenumunit(Dwarf *d, ulong unit, DwarfSym *s)
+{
+	int i;
+	ulong aoff, len;
+
+	if(unit >= d->info.len){
+		werrstr("dwarf unit address 0x%lux >= 0x%lux out of range", unit, d->info.len);
+		return -1;
+	}
+	memset(s, 0, sizeof *s);
+	memset(&s->b, 0, sizeof s->b);
+	s->b.d = d;
+	s->b.p = d->info.data + unit;
+	s->b.ep = d->info.data + d->info.len;
+	len = dwarfget4(&s->b);
+	s->nextunit = unit + 4 + len;
+
+	if(s->b.ep - s->b.p < len){
+	badheader:
+		werrstr("bad dwarf unit header at unit 0x%lux", unit);
+		return -1;
+	}
+	s->b.ep = s->b.p+len;
+	if((i=dwarfget2(&s->b)) != 2)
+		goto badheader;
+	aoff = dwarfget4(&s->b);
+	s->b.addrsize = dwarfget1(&s->b);
+	if(d->addrsize == 0)
+		d->addrsize = s->b.addrsize;
+	if(s->b.p == nil)
+		goto badheader;
+
+	s->aoff = aoff;
+	s->unit = unit;
+	s->depth = 0;
+	return 0;
+}
+
+int
+dwarfenum(Dwarf *d, DwarfSym *s)
+{
+	if(dwarfenumunit(d, 0, s) < 0)
+		return -1;
+	s->allunits = 1;
+	return 0;
+}
+
+static int
+_dwarfnextsym(Dwarf *d, DwarfSym *s)
+{
+	ulong num;
+	DwarfAbbrev *a;
+
+	if(s->attrs.haskids)
+		s->depth++;
+top:
+	if(s->b.p >= s->b.ep){
+		if(s->allunits && s->nextunit < d->info.len){
+			if(dwarfenumunit(d, s->nextunit, s) < 0)
+				return -1;
+			s->allunits = 1;
+			goto top;
+		}
+		return 0;
+	}
+
+	s->uoff = s->b.p - (d->info.data+s->unit);
+	num = dwarfget128(&s->b);
+	if(num == 0){
+		if(s->depth == 0)
+			return 0;
+		if(s->depth > 0)
+			s->depth--;
+		goto top;
+	}
+
+	a = dwarfgetabbrev(d, s->aoff, num);
+	if(a == nil){
+		fprint(2, "getabbrev %ud: %r\n", num);
+		return -1;
+	}
+	if(parseattrs(&s->b, s->unit, a, &s->attrs) < 0)
+		return -1;
+	return 1;
+}
+
+int
+dwarfnextsym(Dwarf *d, DwarfSym *s, int recurse)
+{
+	int r;
+	int depth;
+	ulong sib;
+
+	if(recurse)
+		return _dwarfnextsym(d, s);
+
+	depth = s->depth;
+	if(s->attrs.have.sibling){
+		sib = s->attrs.sibling;
+		if(sib < d->info.len && d->info.data+sib >= s->b.p)
+			s->b.p = d->info.data+sib;
+		s->attrs.haskids = 0;
+	}
+
+	do{
+		r = _dwarfnextsym(d, s);
+		if(r <= 0)
+			return r;
+	}while(s->depth != depth);
+	if(s->depth < depth)
+		return 0;
+	return 1;
+}
+
+typedef struct Parse Parse;
+struct Parse {
+	int name;
+	int off;
+	int haveoff;
+	int type;
+};
+
+#define OFFSET(x) offsetof(DwarfAttrs, x), offsetof(DwarfAttrs, have.x)
+
+static Parse plist[] = {	/* Font Tab 4 */
+	DwarfAttrAbstractOrigin,	OFFSET(abstractorigin),		TReference,
+	DwarfAttrAccessibility,	OFFSET(accessibility),		TConstant,
+	DwarfAttrAddrClass, 		OFFSET(addrclass), 			TConstant,
+	DwarfAttrArtificial,		OFFSET(isartificial), 		TFlag,
+	DwarfAttrBaseTypes,		OFFSET(basetypes),			TReference,
+	DwarfAttrBitOffset,		OFFSET(bitoffset),			TConstant,
+	DwarfAttrBitSize,		OFFSET(bitsize),			TConstant,
+	DwarfAttrByteSize,		OFFSET(bytesize),			TConstant,
+	DwarfAttrCalling,		OFFSET(calling),			TConstant,
+	DwarfAttrCommonRef,		OFFSET(commonref),			TReference,
+	DwarfAttrCompDir,		OFFSET(compdir),			TString,
+	DwarfAttrConstValue,		OFFSET(constvalue),			TString|TConstant|TBlock,
+	DwarfAttrContainingType,	OFFSET(containingtype),		TReference,
+	DwarfAttrCount,			OFFSET(count),				TConstant|TReference,
+	DwarfAttrDataMemberLoc,	OFFSET(datamemberloc),		TBlock|TConstant|TReference,
+	DwarfAttrDeclColumn,		OFFSET(declcolumn),			TConstant,
+	DwarfAttrDeclFile,		OFFSET(declfile),			TConstant,
+	DwarfAttrDeclLine,		OFFSET(declline),			TConstant,
+	DwarfAttrDeclaration,	OFFSET(isdeclaration),		TFlag,
+	DwarfAttrDefaultValue,	OFFSET(defaultvalue),		TReference,
+	DwarfAttrDiscr,			OFFSET(discr),				TReference,
+	DwarfAttrDiscrList,		OFFSET(discrlist),			TBlock,
+	DwarfAttrDiscrValue,		OFFSET(discrvalue),			TConstant,
+	DwarfAttrEncoding,		OFFSET(encoding),			TConstant,
+	DwarfAttrExternal,		OFFSET(isexternal),			TFlag,
+	DwarfAttrFrameBase,		OFFSET(framebase),			TBlock|TConstant,
+	DwarfAttrFriend,			OFFSET(friend),				TReference,
+	DwarfAttrHighpc,			OFFSET(highpc),				TAddress,
+	DwarfAttrIdentifierCase,	OFFSET(identifiercase),		TConstant,
+	DwarfAttrImport,			OFFSET(import),				TReference,
+	DwarfAttrInline,			OFFSET(inlined),			TConstant,
+	DwarfAttrIsOptional,		OFFSET(isoptional),			TFlag,
+	DwarfAttrLanguage,		OFFSET(language),			TConstant,
+	DwarfAttrLocation,		OFFSET(location),			TBlock|TConstant,
+	DwarfAttrLowerBound,		OFFSET(lowerbound),			TConstant|TReference,
+	DwarfAttrLowpc,			OFFSET(lowpc),				TAddress,
+	DwarfAttrMacroInfo,		OFFSET(macroinfo),			TConstant,
+	DwarfAttrName,			OFFSET(name),				TString,
+	DwarfAttrNamelistItem,	OFFSET(namelistitem),		TBlock,
+	DwarfAttrOrdering, 		OFFSET(ordering),			TConstant,
+	DwarfAttrPriority,		OFFSET(priority),			TReference,
+	DwarfAttrProducer,		OFFSET(producer),			TString,
+	DwarfAttrPrototyped,		OFFSET(isprototyped),		TFlag,
+	DwarfAttrRanges,			OFFSET(ranges),				TReference,
+	DwarfAttrReturnAddr,		OFFSET(returnaddr),			TBlock|TConstant,
+	DwarfAttrSegment,		OFFSET(segment),			TBlock|TConstant,
+	DwarfAttrSibling,		OFFSET(sibling),			TReference,
+	DwarfAttrSpecification,	OFFSET(specification),		TReference,
+	DwarfAttrStartScope,		OFFSET(startscope),			TConstant,
+	DwarfAttrStaticLink,		OFFSET(staticlink),			TBlock|TConstant,
+	DwarfAttrStmtList,		OFFSET(stmtlist),			TConstant,
+	DwarfAttrStrideSize,		OFFSET(stridesize),			TConstant,
+	DwarfAttrStringLength,	OFFSET(stringlength),		TBlock|TConstant,
+	DwarfAttrType,			OFFSET(type),				TReference,
+	DwarfAttrUpperBound,		OFFSET(upperbound),			TConstant|TReference,
+	DwarfAttrUseLocation,	OFFSET(uselocation),		TBlock|TConstant,
+	DwarfAttrVarParam,		OFFSET(isvarparam),			TFlag,
+	DwarfAttrVirtuality,		OFFSET(virtuality),			TConstant,
+	DwarfAttrVisibility,		OFFSET(visibility),			TConstant,
+	DwarfAttrVtableElemLoc,	OFFSET(vtableelemloc),		TBlock|TReference,
+};
+
+static Parse ptab[DwarfAttrMax];
+
+static int
+parseattrs(DwarfBuf *b, ulong unit, DwarfAbbrev *a, DwarfAttrs *attrs)
+{
+	int i, f, n, got;
+	static int nbad;
+	void *v;
+
+	/* initialize ptab first time through for quick access */
+	if(ptab[DwarfAttrName].name != DwarfAttrName)
+		for(i=0; i<nelem(plist); i++)
+			ptab[plist[i].name] = plist[i];
+
+	memset(attrs, 0, sizeof *attrs);
+	attrs->tag = a->tag;
+	attrs->haskids = a->haskids;
+
+	for(i=0; i<a->nattr; i++){
+		n = a->attr[i].name;
+		f = a->attr[i].form;
+		if(n < 0 || n >= nelem(ptab) || ptab[n].name==0){
+			if(++nbad == 1)
+				fprint(2, "dwarf parse attrs: unexpected attribute name 0x%ux\n", n);
+			return -1;
+		}
+		v = (char*)attrs + ptab[n].off;
+		got = 0;
+		if(f == FormIndirect)
+			f = dwarfget128(b);
+		if((ptab[n].type&(TConstant|TReference|TAddress))
+		&& getulong(b, f, unit, v, &got) >= 0)
+			;
+		else if((ptab[n].type&TFlag) && getuchar(b, f, v) >= 0)
+			got = TFlag;
+		else if((ptab[n].type&TString) && getstring(b, f, v) >= 0)
+			got = TString;
+		else if((ptab[n].type&TBlock) && getblock(b, f, v) >= 0)
+			got = TBlock;
+		else{
+			if(skipform(b, f) < 0){
+				if(++nbad == 1)
+					fprint(2, "dwarf parse attrs: cannot skip form %d\n", f);
+				return -1;
+			}
+		}
+		if(got == TBlock && (ptab[n].type&TConstant))
+			got = constblock(b->d, v, v);
+		*((uchar*)attrs+ptab[n].haveoff) = got;
+	}
+	return 0;
+}
+
+static int
+getulong(DwarfBuf *b, int form, ulong unit, ulong *u, int *type)
+{
+	static int nbad;
+	uvlong uv;
+
+	switch(form){
+	default:
+		return -1;
+
+	/* addresses */
+	case FormAddr:
+		*type = TAddress;
+		*u = dwarfgetaddr(b);
+		return 0;
+
+	/* references */
+	case FormRefAddr:
+		/* absolute ref in .debug_info */
+		*type = TReference;
+		*u = dwarfgetaddr(b);
+		return 0;
+	case FormRef1:
+		*u = dwarfget1(b);
+		goto relativeref;
+	case FormRef2:
+		*u = dwarfget2(b);
+		goto relativeref;
+	case FormRef4:
+		*u = dwarfget4(b);
+		goto relativeref;
+	case FormRef8:
+		*u = dwarfget8(b);
+		goto relativeref;
+	case FormRefUdata:
+		*u = dwarfget128(b);
+	relativeref:
+		*u += unit;
+		*type = TReference;
+		return 0;
+
+	/* constants */
+	case FormData1:
+		*u = dwarfget1(b);
+		goto constant;
+	case FormData2:
+		*u = dwarfget2(b);
+		goto constant;
+	case FormData4:
+		*u = dwarfget4(b);
+		goto constant;
+	case FormData8:
+		uv = dwarfget8(b);
+		*u = uv;
+		if(uv != *u && ++nbad == 1)
+			fprint(2, "dwarf: truncating 64-bit attribute constants\n");
+		goto constant;
+	case FormSdata:
+		*u = dwarfget128s(b);
+		goto constant;
+	case FormUdata:
+		*u = dwarfget128(b);
+	constant:
+		*type = TConstant;
+		return 0;
+	}
+}
+
+static int
+getuchar(DwarfBuf *b, int form, uchar *u)
+{
+	switch(form){
+	default:
+		return -1;
+
+	case FormFlag:
+		*u = dwarfget1(b);
+		return 0;
+	}
+}
+
+static int
+getstring(DwarfBuf *b, int form, char **s)
+{
+	static int nbad;
+	ulong u;
+
+	switch(form){
+	default:
+		return -1;
+
+	case FormString:
+		*s = dwarfgetstring(b);
+		return 0;
+
+	case FormStrp:
+		u = dwarfget4(b);
+		if(u >= b->d->str.len){
+			if(++nbad == 1)
+				fprint(2, "dwarf: bad string pointer 0x%lux in attribute\n", u);
+			/* don't return error - maybe can proceed */
+			*s = nil;
+		}else
+			*s = b->d->str.data + u;
+		return 0;
+
+	}
+}
+
+static int
+getblock(DwarfBuf *b, int form, DwarfBlock *bl)
+{
+	ulong n;
+
+	switch(form){
+	default:
+		return -1;
+	case FormDwarfBlock:
+		n = dwarfget128(b);
+		goto copyn;
+	case FormDwarfBlock1:
+		n = dwarfget1(b);
+		goto copyn;
+	case FormDwarfBlock2:
+		n = dwarfget2(b);
+		goto copyn;
+	case FormDwarfBlock4:
+		n = dwarfget4(b);
+	copyn:
+		bl->data = dwarfgetnref(b, n);
+		bl->len = n;
+		if(bl->data == nil)
+			return -1;
+		return 0;
+	}
+}
+
+static int
+constblock(Dwarf *d, DwarfBlock *bl, ulong *pval)
+{
+	DwarfBuf b;
+
+	memset(&b, 0, sizeof b);
+	b.p = bl->data;
+	b.ep = bl->data+bl->len;
+	b.d = d;
+
+	switch(dwarfget1(&b)){
+	case OpAddr:
+		*pval = dwarfgetaddr(&b);
+		return TConstant;
+	case OpConst1u:
+		*pval = dwarfget1(&b);
+		return TConstant;
+	case OpConst1s:
+		*pval = (schar)dwarfget1(&b);
+		return TConstant;
+	case OpConst2u:
+		*pval = dwarfget2(&b);
+		return TConstant;
+	case OpConst2s:
+		*pval = (s16int)dwarfget2(&b);
+		return TConstant;
+	case OpConst4u:
+		*pval = dwarfget4(&b);
+		return TConstant;
+	case OpConst4s:
+		*pval = (s32int)dwarfget4(&b);
+		return TConstant;
+	case OpConst8u:
+		*pval = (u64int)dwarfget8(&b);
+		return TConstant;
+	case OpConst8s:
+		*pval = (s64int)dwarfget8(&b);
+		return TConstant;
+	case OpConstu:
+		*pval = dwarfget128(&b);
+		return TConstant;
+	case OpConsts:
+		*pval = dwarfget128s(&b);
+		return TConstant;
+	case OpPlusUconst:
+		*pval = dwarfget128(&b);
+		return TConstant;
+	default:
+		return TBlock;
+	}
+}
+
+/* last resort */
+static int
+skipform(DwarfBuf *b, int form)
+{
+	int type;
+	DwarfVal val;
+
+	if(getulong(b, form, 0, &val.c, &type) < 0
+	&& getuchar(b, form, (uchar*)&val) < 0
+	&& getstring(b, form, &val.s) < 0
+	&& getblock(b, form, &val.b) < 0)
+		return -1;
+	return 0;
+}
blob - /dev/null
blob + 6122ae683c887f471fa6fa84449a1749812a1f4e (mode 644)
--- /dev/null
+++ src/libmach/dwarfopen.c
@@ -0,0 +1,107 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int
+readblock(int fd, DwarfBlock *b, ulong off, ulong len)
+{
+	b->data = malloc(len);
+	if(b->data == nil)
+		return -1;
+	if(seek(fd, off, 0) < 0 || readn(fd, b->data, len) != len){
+		free(b->data);
+		b->data = nil;
+		return -1;
+	}
+	b->len = len;
+	return 0;
+}
+
+static int
+findsection(Elf *elf, char *name, ulong *off, ulong *len)
+{
+	ElfSect *s;
+
+	if((s = elfsection(elf, name)) == nil)
+		return -1;
+	*off = s->offset;
+	*len = s->size;
+	return s - elf->sect;
+}
+	
+static int
+loadsection(Elf *elf, char *name, DwarfBlock *b)
+{
+	ulong off, len;
+
+	if(findsection(elf, name, &off, &len) < 0)
+		return -1;
+	return readblock(elf->fd, b, off, len);
+}
+
+Dwarf*
+dwarfopen(Elf *elf)
+{
+	Dwarf *d;
+
+	if(elf == nil){
+		werrstr("nil elf passed to dwarfopen");
+		return nil;
+	}
+
+	d = mallocz(sizeof(Dwarf), 1);
+	if(d == nil)
+		return nil;
+
+	d->elf = elf;
+	if(loadsection(elf, ".debug_abbrev", &d->abbrev) < 0
+	|| loadsection(elf, ".debug_aranges", &d->aranges) < 0
+	|| loadsection(elf, ".debug_frame", &d->frame) < 0
+	|| loadsection(elf, ".debug_line", &d->line) < 0
+	|| loadsection(elf, ".debug_pubnames", &d->pubnames) < 0
+	|| loadsection(elf, ".debug_ranges", &d->ranges) < 0
+	|| loadsection(elf, ".debug_str", &d->str) < 0
+	|| loadsection(elf, ".debug_info", &d->info) < 0)
+		goto err;
+
+	/* make this a table once there are more */
+	switch(d->elf->hdr.machine){
+	case ElfMach386:
+		d->reg = dwarf386regs;
+		d->nreg = dwarf386nregs;
+		break;
+	default:
+		werrstr("unsupported machine");
+		goto err;
+	}
+
+	return d;
+
+err:
+	free(d->abbrev.data);
+	free(d->aranges.data);
+	free(d->frame.data);
+	free(d->line.data);
+	free(d->pubnames.data);
+	free(d->ranges.data);
+	free(d->str.data);
+	free(d->info.data);
+	free(d);
+	return nil;
+}
+
+void
+dwarfclose(Dwarf *d)
+{
+	free(d->abbrev.data);
+	free(d->aranges.data);
+	free(d->frame.data);
+	free(d->line.data);
+	free(d->pubnames.data);
+	free(d->ranges.data);
+	free(d->str.data);
+	free(d->info.data);
+	free(d);
+}
blob - /dev/null
blob + 165b3aa017727d1befc65f9e01f1ec49fc9d86d9 (mode 644)
--- /dev/null
+++ src/libmach/dwarfpc.c
@@ -0,0 +1,338 @@
+/*
+ * Dwarf pc to source line conversion.
+ * 
+ * Maybe should do the reverse here, but what should the interface look like?
+ * One possibility is to use the Plan 9 line2addr interface:
+ *
+ *	long line2addr(ulong line, ulong basepc)
+ *
+ * which returns the smallest pc > basepc with line number line (ignoring file name).
+ *
+ * The encoding may be small, but it sure isn't simple!
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+#define trace 0
+
+enum
+{
+	Isstmt = 1<<0,
+	BasicDwarfBlock = 1<<1,
+	EndSequence = 1<<2,
+	PrologueEnd = 1<<3,
+	EpilogueBegin = 1<<4,
+};
+
+typedef struct State State;
+struct State
+{
+	ulong addr;
+	ulong file;
+	ulong line;
+	ulong column;
+	ulong flags;
+	ulong isa;
+};
+
+int
+dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, ulong *line, ulong *mtime, ulong *length)
+{
+	uchar *prog, *opcount, *end;
+	ulong off, unit, len, vers, x, start;
+	int i, first, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf;
+	char *files, *dirs, *s;
+	DwarfBuf b;
+	DwarfSym sym;
+	State emit, cur, reset;
+	uchar **f, **newf;
+
+	f = nil;
+
+	if(dwarfaddrtounit(d, pc, &unit) < 0
+	|| dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0)
+		return -1;
+
+	if(!sym.attrs.have.stmtlist){
+		werrstr("no line mapping information for 0x%lux", pc);
+		return -1;
+	}
+	off = sym.attrs.stmtlist;
+	if(off >= d->line.len){
+		fprint(2, "bad stmtlist\n");
+		goto bad;
+	}
+
+	if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist);
+
+	memset(&b, 0, sizeof b);
+	b.d = d;
+	b.p = d->line.data + off;
+	b.ep = b.p + d->line.len;
+	b.addrsize = sym.b.addrsize;	/* should i get this from somewhere else? */
+
+	len = dwarfget4(&b);
+	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
+		fprint(2, "bad len\n");
+		goto bad;
+	}
+
+	b.ep = b.p+len;
+	vers = dwarfget2(&b);
+	if(vers != 2){
+		werrstr("bad dwarf version 0x%lux", vers);
+		return -1;
+	}
+
+	len = dwarfget4(&b);
+	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
+		fprint(2, "another bad len\n");
+		goto bad;
+	}
+	prog = b.p+len;
+
+	quantum = dwarfget1(&b);
+	isstmt = dwarfget1(&b);
+	linebase = (schar)dwarfget1(&b);
+	linerange = (schar)dwarfget1(&b);
+	opcodebase = dwarfget1(&b);
+
+	opcount = b.p-1;
+	dwarfgetnref(&b, opcodebase-1);
+	if(b.p == nil){
+		fprint(2, "bad opcode chart\n");
+		goto bad;
+	}
+
+	/* just skip the files and dirs for now; we'll come back */
+	dirs = b.p;
+	while(b.p!=nil && *b.p!=0)
+		dwarfgetstring(&b);
+	dwarfget1(&b);
+
+	files = b.p;
+	while(b.p!=nil && *b.p!=0){
+		dwarfgetstring(&b);
+		dwarfget128(&b);
+		dwarfget128(&b);
+		dwarfget128(&b);
+	}
+	dwarfget1(&b);
+
+	/* move on to the program */
+	if(b.p == nil || b.p > prog){
+		fprint(2, "bad header\n");
+		goto bad;
+	}
+	b.p = prog;
+
+	reset.addr = 0;
+	reset.file = 1;
+	reset.line = 1;
+	reset.column = 0;
+	reset.flags = isstmt ? Isstmt : 0;
+	reset.isa = 0;
+
+	cur = reset;
+	emit = reset;
+	nf = 0;
+	start = 0;
+	if(trace) fprint(2, "program @ %lud ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
+	first = 1;
+	while(b.p != nil){
+		op = dwarfget1(&b);
+		if(trace) fprint(2, "\tline %lud, addr 0x%lux, op %d %.10H", cur.line, cur.addr, op, b.p);
+		if(op >= opcodebase){
+			a = (op - opcodebase) / linerange;
+			l = (op - opcodebase) % linerange + linebase;
+			cur.line += l;
+			cur.addr += a * quantum;
+			if(trace) fprint(2, " +%d,%d\n", a, l);
+		emit:
+			if(first){
+				if(cur.addr > pc){
+					werrstr("found wrong line mapping 0x%lux for pc 0x%lux", cur.addr, pc);
+					goto out;
+				}
+				first = 0;
+				start = cur.addr;
+			}
+			if(cur.addr > pc)
+				break;
+			if(b.p == nil){
+				werrstr("buffer underflow in line mapping");
+				goto out;
+			}
+			emit = cur;
+			if(emit.flags & EndSequence){
+				werrstr("found wrong line mapping 0x%lux-0x%lux for pc 0x%lux", start, cur.addr, pc);
+				goto out;
+			}
+			cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
+		}else{
+			switch(op){
+			case 0:	/* extended op code */
+				if(trace) fprint(2, " ext");
+				len = dwarfget128(&b);
+				end = b.p+len;
+				if(b.p == nil || end > b.ep || end < b.p || len < 1)
+					goto bad;
+				switch(dwarfget1(&b)){
+				case 1:	/* end sequence */
+					if(trace) fprint(2, " end\n");
+					cur.flags |= EndSequence;
+					goto emit;
+				case 2:	/* set address */
+					cur.addr = dwarfgetaddr(&b);
+					if(trace) fprint(2, " set pc 0x%lux\n", cur.addr);
+					break;
+				case 3:	/* define file */
+					newf = realloc(f, (nf+1)*sizeof(f[0]));
+					if(newf == nil)
+						goto out;
+					f[nf++] = b.p;
+					s = dwarfgetstring(&b);
+					dwarfget128(&b);
+					dwarfget128(&b);
+					dwarfget128(&b);
+					if(trace) fprint(2, " def file %s\n", s);
+					break;
+				}
+				if(b.p == nil || b.p > end)
+					goto bad;
+				b.p = end;
+				break;
+			case 1:	/* emit */
+				if(trace) fprint(2, " emit\n");
+				goto emit;
+			case 2:	/* advance pc */
+				a = dwarfget128(&b);
+				if(trace) fprint(2, " advance pc + %lud\n", a*quantum);
+				cur.addr += a * quantum;
+				break;
+			case 3:	/* advance line */
+				l = dwarfget128s(&b);
+				if(trace) fprint(2, " advance line + %ld\n", l);
+				cur.line += l;
+				break;
+			case 4:	/* set file */
+				if(trace) fprint(2, " set file\n");
+				cur.file = dwarfget128s(&b);
+				break;
+			case 5:	/* set column */
+				if(trace) fprint(2, " set column\n");
+				cur.column = dwarfget128(&b);
+				break;
+			case 6:	/* negate stmt */
+				if(trace) fprint(2, " negate stmt\n");
+				cur.flags ^= Isstmt;
+				break;
+			case 7:	/* set basic block */
+				if(trace) fprint(2, " set basic block\n");
+				cur.flags |= BasicDwarfBlock;
+				break;
+			case 8:	/* const add pc */
+				a = (255 - opcodebase) / linerange * quantum;
+				if(trace) fprint(2, " const add pc + %d\n", a);
+				cur.addr += a;
+				break;
+			case 9:	/* fixed advance pc */
+				a = dwarfget2(&b);
+				if(trace) fprint(2, " fixed advance pc + %d\n", a);
+				cur.addr += a;
+				break;
+			case 10:	/* set prologue end */
+				if(trace) fprint(2, " set prologue end\n");
+				cur.flags |= PrologueEnd;
+				break;
+			case 11:	/* set epilogue begin */
+				if(trace) fprint(2, " set epilogue begin\n");
+				cur.flags |= EpilogueBegin;
+				break;
+			case 12:	/* set isa */
+				if(trace) fprint(2, " set isa\n");
+				cur.isa = dwarfget128(&b);
+				break;
+			default:	/* something new - skip it */
+				if(trace) fprint(2, " unknown %d\n", opcount[op]);
+				for(i=0; i<opcount[op]; i++)
+					dwarfget128(&b);
+				break;
+			}
+		}
+	}
+	if(b.p == nil)
+		goto bad;
+
+	/* finally!  the data we seek is in "emit" */
+
+	if(emit.file == 0){
+		werrstr("invalid file index in mapping data");
+		goto out;
+	}
+	if(line)
+		*line = emit.line;
+
+	/* skip over first emit.file-2 guys */
+	b.p = files;
+	for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
+		dwarfgetstring(&b);
+		dwarfget128(&b);
+		dwarfget128(&b);
+		dwarfget128(&b);
+	}
+	if(b.p == nil){
+		werrstr("problem parsing file data second time (cannot happen)");
+		goto bad;
+	}
+	if(*b.p == 0){
+		if(i >= nf){
+			werrstr("bad file index in mapping data");
+			goto bad;
+		}
+		b.p = f[i];
+	}
+	s = dwarfgetstring(&b);
+	if(file)
+		*file = s;
+	i = dwarfget128(&b);		/* directory */
+	x = dwarfget128(&b);
+	if(mtime)
+		*mtime = x;
+	x = dwarfget128(&b);
+	if(length)
+		*length = x;
+
+	/* fetch dir name */
+	if(cdir)
+		*cdir = sym.attrs.compdir;
+
+	if(dir){
+		if(i == 0)
+			*dir = nil;
+		else{
+			b.p = dirs;
+			for(i--; i>0 && b.p!=nil && *b.p!=0; i--)
+				dwarfgetstring(&b);
+			if(b.p==nil || *b.p==0){
+				werrstr("bad directory reference in line mapping");
+				goto out;		/* can only happen with bad dir index */
+			}
+			*dir = dwarfgetstring(&b);
+		}
+	}
+
+	/* free at last, free at last */
+	free(f);
+	return 0;
+
+bad:
+	werrstr("corrupted line mapping for 0x%lux", pc);
+out:
+	free(f);
+	return -1;
+}
blob - /dev/null
blob + b09d2866dea642e8f679d85259ee3a62967a93d8 (mode 644)
--- /dev/null
+++ src/libmach/dwarfpubnames.c
@@ -0,0 +1,76 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static int
+_dwarfnametounit(Dwarf *d, char *name, DwarfBlock *bl, DwarfSym *s)
+{
+	int vers;
+	ulong len, unit, off;
+	uchar *next;
+	char *str;
+	DwarfBuf b;
+
+	b.d = d;
+	b.p = bl->data;
+	b.ep = b.p + bl->len;
+
+	while(b.p < b.ep){
+		len = dwarfget4(&b);
+		if(len > b.ep-b.p){
+			werrstr("bad length in dwarf name header");
+			return -1;
+		}
+		next = b.p + len;
+		vers = dwarfget2(&b);
+		if(vers != 1 && vers != 2){
+			werrstr("bad version %d in dwarf name header", vers);
+			return -1;
+		}
+		unit = dwarfget4(&b);
+		dwarfget4(&b);	/* unit length */
+		while(b.p < next){
+			off = dwarfget4(&b);
+			if(off == 0)
+				break;
+			str = dwarfgetstring(&b);
+			if(strcmp(str, name) == 0){
+				if(dwarfenumunit(d, unit, s) < 0)
+					return -1;
+				if(unit + off >= s->b.ep - d->info.data){
+					werrstr("bad offset in name entry");
+					return -1;
+				}
+				s->b.p = d->info.data + unit + off;
+				if(dwarfnextsym(d, s, 1) < 0)
+					return -1;
+				if(s->attrs.name==nil || strcmp(s->attrs.name, name)!=0){
+					werrstr("unexpected name %#q in lookup for %#q", s->attrs.name, name);
+					return -1;
+				}
+				return 0;
+			}
+		}
+		b.p = next;
+	}
+	werrstr("unknown name '%s'", name);
+	return -1;
+}
+
+int
+dwarflookupname(Dwarf *d, char *name, DwarfSym *sym)
+{
+	return _dwarfnametounit(d, name, &d->pubnames, sym);
+}
+
+/*
+
+int
+dwarflookuptype(Dwarf *d, char *name, DwarfSym *sym)
+{
+	return _dwarfnametounit(d, name, &d->pubtypes, sym);
+}
+
+ */
blob - /dev/null
blob + 6a387fd3bb2d2444782d108e96567e48a8f7aef3 (mode 644)
--- /dev/null
+++ src/libmach/elf.c
@@ -0,0 +1,405 @@
+/*
+ * Parse 32-bit ELF files.
+ * Copyright (c) 2004 Russ Cox.  See LICENSE.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+
+typedef struct ElfHdrBytes ElfHdrBytes;
+typedef struct ElfSectBytes ElfSectBytes;
+typedef struct ElfProgBytes ElfProgBytes;
+typedef struct ElfSymBytes ElfSymBytes;
+
+struct ElfHdrBytes
+{
+	uchar	ident[16];
+	uchar	type[2];
+	uchar	machine[2];
+	uchar	version[4];
+	uchar	entry[4];
+	uchar	phoff[4];
+	uchar	shoff[4];
+	uchar	flags[4];
+	uchar	ehsize[2];
+	uchar	phentsize[2];
+	uchar	phnum[2];
+	uchar	shentsize[2];
+	uchar	shnum[2];
+	uchar	shstrndx[2];
+};
+
+struct ElfSectBytes
+{
+	uchar	name[4];
+	uchar	type[4];
+	uchar	flags[4];
+	uchar	addr[4];
+	uchar	offset[4];
+	uchar	size[4];
+	uchar	link[4];
+	uchar	info[4];
+	uchar	align[4];
+	uchar	entsize[4];
+};
+
+struct ElfSymBytes
+{
+	uchar	name[4];
+	uchar	value[4];
+	uchar	size[4];
+	uchar	info;	/* top4: bind, bottom4: type */
+	uchar	other;
+	uchar	shndx[2];
+};
+
+struct ElfProgBytes
+{
+	uchar	type[4];
+	uchar	offset[4];
+	uchar	vaddr[4];
+	uchar	paddr[4];
+	uchar	filesz[4];
+	uchar	memsz[4];
+	uchar	flags[4];
+	uchar	align[4];
+};
+
+uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' };
+
+static void	unpackhdr(ElfHdr*, ElfHdrBytes*);
+static void	unpackprog(ElfHdr*, ElfProg*, ElfProgBytes*);
+static void	unpacksect(ElfHdr*, ElfSect*, ElfSectBytes*);
+
+static char *elftypes[] = {
+	"none",
+	"relocatable",
+	"executable",
+	"shared object",
+	"core",
+};
+
+char*
+elftype(int t)
+{
+	if(t < 0 || t >= nelem(elftypes))
+		return "unknown";
+	return elftypes[t];
+}
+
+static char *elfmachs[] = {
+	"none",
+	"32100",
+	"sparc",
+	"386",
+	"68000",
+	"88000",
+	"486",
+	"860",
+	"MIPS",
+};
+
+char*
+elfmachine(int t)
+{
+	if(t < 0 || t >= nelem(elfmachs))
+		return "unknown";
+	return elfmachs[t];
+}
+
+Elf*
+elfopen(char *name)
+{
+	int fd;
+	Elf *e;
+
+	if((fd = open(name, OREAD)) < 0)
+		return nil;
+	if((e = elfinit(fd)) == nil)
+		close(fd);
+	return e;
+}
+
+Elf*
+elfinit(int fd)
+{
+	int i;
+	Elf *e;
+	ElfHdr *h;
+	ElfHdrBytes hdrb;
+	ElfProgBytes progb;
+	ElfSectBytes sectb;
+	ElfSect *s;
+
+	e = mallocz(sizeof(Elf), 1);
+	if(e == nil)
+		return nil;
+	e->fd = fd;
+
+	/*
+	 * parse header
+	 */
+	seek(fd, 0, 0);
+	if(readn(fd, &hdrb, sizeof hdrb) != sizeof hdrb)
+		goto err;
+	h = &e->hdr;
+	unpackhdr(h, &hdrb);
+	if(h->class != ElfClass32){
+		werrstr("bad ELF class - not 32-bit");
+		goto err;
+	}
+	if(h->encoding != ElfDataLsb && h->encoding != ElfDataMsb){
+		werrstr("bad ELF encoding - not LSB, MSB");
+		goto err;
+	}
+	if(hdrb.ident[6] != h->version){
+		werrstr("bad ELF encoding - version mismatch %02ux and %08ux",
+			(uint)hdrb.ident[6], (uint)h->version);
+		goto err;
+	}
+
+	/*
+	 * the prog+section info is almost always small - just load it into memory.
+	 */
+	e->nprog = h->phnum;
+	e->prog = mallocz(sizeof(ElfProg)*e->nprog, 1);
+	for(i=0; i<e->nprog; i++){
+		if(seek(fd, h->phoff+i*h->phentsize, 0) < 0
+		|| readn(fd, &progb, sizeof progb) != sizeof progb)
+			goto err;
+		unpackprog(h, &e->prog[i], &progb);
+	}
+
+	e->nsect = h->shnum;
+	if(e->nsect == 0)
+		goto nosects;
+	e->sect = mallocz(sizeof(ElfSect)*e->nsect, 1);
+	for(i=0; i<e->nsect; i++){
+		if(seek(fd, h->shoff+i*h->shentsize, 0) < 0
+		|| readn(fd, &sectb, sizeof sectb) != sizeof sectb)
+			goto err;
+		unpacksect(h, &e->sect[i], &sectb);
+	}
+
+	if(h->shstrndx >= e->nsect){
+		fprint(2, "warning: bad string section index %d >= %d", h->shstrndx, e->nsect);
+		h->shnum = 0;
+		e->nsect = 0;
+		goto nosects;
+	}
+	s = &e->sect[h->shstrndx];
+	if(elfmap(e, s) < 0)
+		goto err;
+
+	for(i=0; i<e->nsect; i++)
+		if(e->sect[i].name)
+			e->sect[i].name = s->base + (ulong)e->sect[i].name;
+
+	e->symtab = elfsection(e, ".symtab");
+	if(e->symtab){
+		if(e->symtab->link >= e->nsect)
+			e->symtab = nil;
+		else{
+			e->symstr = &e->sect[e->symtab->link];
+			e->nsymtab = e->symtab->size / sizeof(ElfSymBytes);
+		}
+	}
+	e->dynsym = elfsection(e, ".dynsym");
+	if(e->dynsym){
+		if(e->dynsym->link >= e->nsect)
+			e->dynsym = nil;
+		else{
+			e->dynstr = &e->sect[e->dynsym->link];
+			e->ndynsym = e->dynsym->size / sizeof(ElfSymBytes);
+		}
+	}
+
+	e->bss = elfsection(e, ".bss");
+
+nosects:
+	return e;
+
+err:
+	free(e->sect);
+	free(e->prog);
+	free(e->shstrtab);
+	free(e);
+	return nil;
+}
+
+void
+elfclose(Elf *elf)
+{
+	int i;
+
+	for(i=0; i<elf->nsect; i++)
+		free(elf->sect[i].base);
+	free(elf->sect);
+	free(elf->prog);
+	free(elf->shstrtab);
+	free(elf);
+}
+
+static void
+unpackhdr(ElfHdr *h, ElfHdrBytes *b)
+{
+	u16int (*e2)(uchar*);
+	u32int (*e4)(uchar*);
+	u64int (*e8)(uchar*);
+
+	memmove(h->magic, b->ident, 4);
+	h->class = b->ident[4];
+	h->encoding = b->ident[5];
+	switch(h->encoding){
+	case ElfDataLsb:
+		e2 = leload2;
+		e4 = leload4;
+		e8 = leload8;
+		break;
+	case ElfDataMsb:
+		e2 = beload2;
+		e4 = beload4;
+		e8 = beload8;
+		break;
+	default:
+		return;
+	}
+	h->abi = b->ident[7];
+	h->abiversion = b->ident[8];
+
+	h->e2 = e2;
+	h->e4 = e4;
+	h->e8 = e8;
+	
+	h->type = e2(b->type);
+	h->machine = e2(b->machine);
+	h->version = e4(b->version);
+	h->entry = e4(b->entry);
+	h->phoff = e4(b->phoff);
+	h->shoff = e4(b->shoff);
+	h->flags = e4(b->flags);
+	h->ehsize = e2(b->ehsize);
+	h->phentsize = e2(b->phentsize);
+	h->phnum = e2(b->phnum);
+	h->shentsize = e2(b->shentsize);
+	h->shnum = e2(b->shnum);
+	h->shstrndx = e2(b->shstrndx);
+}
+
+static void
+unpackprog(ElfHdr *h, ElfProg *p, ElfProgBytes *b)
+{
+	u32int (*e4)(uchar*);
+
+	e4 = h->e4;
+	p->type = e4(b->type);
+	p->offset = e4(b->offset);
+	p->vaddr = e4(b->vaddr);
+	p->paddr = e4(b->paddr);
+	p->filesz = e4(b->filesz);
+	p->memsz = e4(b->memsz);
+	p->flags = e4(b->flags);
+	p->align = e4(b->align);
+}
+
+static void
+unpacksect(ElfHdr *h, ElfSect *s, ElfSectBytes *b)
+{
+	u32int (*e4)(uchar*);
+
+	e4 = h->e4;
+	s->name = (char*)e4(b->name);
+	s->type = e4(b->type);
+	s->flags = e4(b->flags);
+	s->addr = e4(b->addr);
+	s->offset = e4(b->offset);
+	s->size = e4(b->size);
+	s->link = e4(b->link);
+	s->info = e4(b->info);
+	s->align = e4(b->align);
+	s->entsize = e4(b->entsize);
+}
+
+ElfSect*
+elfsection(Elf *elf, char *name)
+{
+	int i;
+
+	for(i=0; i<elf->nsect; i++){
+		if(elf->sect[i].name == name)
+			return &elf->sect[i];
+		if(elf->sect[i].name && name
+		&& strcmp(elf->sect[i].name, name) == 0)
+			return &elf->sect[i];
+	}
+	werrstr("elf section '%s' not found", name);
+	return nil;
+}
+
+int
+elfmap(Elf *elf, ElfSect *sect)
+{
+	if(sect->base)
+		return 0;
+	if((sect->base = malloc(sect->size)) == nil)
+		return -1;
+	werrstr("short read");
+	if(seek(elf->fd, sect->offset, 0) < 0
+	|| readn(elf->fd, sect->base, sect->size) != sect->size){
+		free(sect->base);
+		sect->base = nil;
+		return -1;
+	}
+	return 0;
+}
+
+int
+elfsym(Elf *elf, int i, ElfSym *sym)
+{
+	ElfSect *symtab, *strtab;
+	uchar *p;
+	char *s;
+	ulong x;
+
+	if(i < 0){
+		werrstr("bad index %d in elfsym", i);
+		return -1;
+	}
+
+	if(i < elf->nsymtab){
+		symtab = elf->symtab;
+		strtab = elf->symstr;
+	extract:
+		if(elfmap(elf, symtab) < 0 || elfmap(elf, strtab) < 0)
+			return -1;
+		p = symtab->base + i * sizeof(ElfSymBytes);
+		s = strtab->base;
+		x = elf->hdr.e4(p);
+		if(x >= strtab->size){
+			werrstr("bad symbol name offset 0x%lux", x);
+			return -1;
+		}
+		sym->name = s + x;
+		sym->value = elf->hdr.e4(p+4);
+		sym->size = elf->hdr.e4(p+8);
+		x = p[12];
+		sym->bind = x>>4;
+		sym->type = x & 0xF;
+		sym->other = p[13];
+		sym->shndx = elf->hdr.e2(p+14);
+		return 0;
+	}
+	i -= elf->nsymtab;
+	if(i < elf->ndynsym){
+		symtab = elf->dynsym;
+		strtab = elf->dynstr;
+		goto extract;
+	}
+	/* i -= elf->ndynsym */
+
+	werrstr("symbol index out of range");
+	return -1;
+}
+
blob - /dev/null
blob + 6ed239bc2ce4ba9d2d6ccdfa51a296832423182d (mode 644)
--- /dev/null
+++ src/libmach/elf.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2004 Russ Cox.  See LICENSE.
+ */
+
+/* /home/rsc/papers/elfXXelf.pdf */
+
+typedef struct Elf Elf;
+typedef struct ElfHdr ElfHdr;
+typedef struct ElfSect ElfSect;
+typedef struct ElfProg ElfProg;
+typedef struct ElfNote ElfNote;
+typedef struct ElfSym ElfSym;
+
+enum
+{
+	ElfClassNone = 0,
+	ElfClass32,
+	ElfClass64,
+
+	ElfDataNone = 0,
+	ElfDataLsb,
+	ElfDataMsb,
+
+	ElfTypeNone = 0,
+	ElfTypeRelocatable,
+	ElfTypeExecutable,
+	ElfTypeSharedObject,
+	ElfTypeCore,
+	/* 0xFF00 - 0xFFFF reserved for processor-specific types */
+
+	ElfMachNone = 0,
+	ElfMach32100,		/* AT&T WE 32100 */
+	ElfMachSparc,		/* SPARC */
+	ElfMach386,		/* Intel 80386 */
+	ElfMach68000,		/* Motorola 68000 */
+	ElfMach88000,		/* Motorola 88000 */
+	ElfMach486,		/* Intel 80486, no longer used */
+	ElfMach860,		/* Intel 80860 */
+	ElfMachMips,		/* MIPS RS3000 */
+	ElfMachS370,		/* IBM System/370 */
+	ElfMachMipsLe,	/* MIPS RS3000 LE */
+	ElfMachParisc = 15,		/* HP PA RISC */
+	ElfMachVpp500 = 17,	/* Fujitsu VPP500 */
+	ElfMachSparc32Plus,	/* SPARC V8+ */
+	ElfMach960,		/* Intel 80960 */
+	ElfMachPower,		/* PowerPC */
+	ElfMachPower64,	/* PowerPC 64 */
+	ElfMachS390,		/* IBM System/390 */
+	ElfMachV800 = 36,	/* NEC V800 */
+	ElfMachFr20,		/* Fujitsu FR20 */
+	ElfMachRh32,		/* TRW RH-32 */
+	ElfMachRce,		/* Motorola RCE */
+	ElfMachArm,		/* ARM */
+	ElfMachAlpha,		/* Digital Alpha */
+	ElfMachSH,		/* Hitachi SH */
+	ElfMachSparc9,		/* SPARC V9 */
+	/* and the list goes on... */
+
+	ElfAbiNone = 0,
+	ElfAbiSystemV = 0,	/* [sic] */
+	ElfAbiHPUX,
+	ElfAbiNetBSD,
+	ElfAbiLinux,
+	ElfAbiSolaris = 6,
+	ElfAbiAix,
+	ElfAbiIrix,
+	ElfAbiFreeBSD,
+	ElfAbiTru64,
+	ElfAbiModesto,
+	ElfAbiOpenBSD,
+	ElfAbiARM = 97,
+	ElfAbiEmbedded = 255,
+
+	/* some of sections 0xFF00 - 0xFFFF reserved for various things */
+	ElfSectNone = 0,
+	ElfSectProgbits,
+	ElfSectSymtab,
+	ElfSectStrtab,
+	ElfSectRela,
+	ElfSectHash,
+	ElfSectDynamic,
+	ElfSectNote,
+	ElfSectNobits,
+	ElfSectRel,
+	ElfSectShlib,
+	ElfSectDynsym,
+
+	ElfSectFlagWrite = 0x1,
+	ElfSectFlagAlloc = 0x2,
+	ElfSectFlagExec = 0x4,
+	/* 0xF0000000 are reserved for processor specific */
+
+	ElfSymBindLocal = 0,
+	ElfSymBindGlobal,
+	ElfSymBindWeak,
+	/* 13-15 reserved */
+
+	ElfSymTypeNone = 0,
+	ElfSymTypeObject,
+	ElfSymTypeFunc,
+	ElfSymTypeSection,
+	ElfSymTypeFile,
+	/* 13-15 reserved */
+
+	ElfSymShnNone = 0,
+	ElfSymShnAbs = 0xFFF1,
+	ElfSymShnCommon = 0xFFF2,
+	/* 0xFF00-0xFF1F reserved for processors */
+	/* 0xFF20-0xFF3F reserved for operating systems */
+
+	ElfProgNone = 0,
+	ElfProgLoad,
+	ElfProgDynamic,
+	ElfProgInterp,
+	ElfProgNote,
+	ElfProgShlib,
+	ElfProgPhdr,
+
+	ElfProgFlagExec = 0x1,
+	ElfProgFlagWrite = 0x2,
+	ElfProgFlagRead = 0x4,
+
+	ElfNotePrStatus = 1,
+	ElfNotePrFpreg = 2,
+	ElfNotePrPsinfo = 3,
+	ElfNotePrTaskstruct = 4,
+	ElfNotePrAuxv = 6,
+	ElfNotePrXfpreg = 0x46e62b7f,	/* for gdb/386 */
+};
+
+struct ElfHdr
+{
+	uchar	magic[4];
+	uchar	class;
+	uchar	encoding;
+	uchar	version;
+	uchar	abi;
+	uchar	abiversion;
+	u32int	type;
+	u32int	machine;
+	u32int	entry;
+	u32int	phoff;
+	u32int	shoff;
+	u32int	flags;
+	u32int	ehsize;
+	u32int	phentsize;
+	u32int	phnum;
+	u32int	shentsize;
+	u32int	shnum;
+	u32int	shstrndx;
+	u16int	(*e2)(uchar*);
+	u32int	(*e4)(uchar*);
+	u64int	(*e8)(uchar*);
+};
+
+struct ElfSect
+{
+	char		*name;
+	u32int	type;
+	u32int	flags;
+	u32int	addr;
+	u32int	offset;
+	u32int	size;
+	u32int	link;
+	u32int	info;
+	u32int	align;
+	u32int	entsize;
+	uchar	*base;
+};
+
+struct ElfProg
+{
+	u32int	type;
+	u32int	offset;
+	u32int	vaddr;
+	u32int	paddr;
+	u32int	filesz;
+	u32int	memsz;
+	u32int	flags;
+	u32int	align;
+};
+
+struct ElfNote
+{
+	u32int	namesz;
+	u32int	descsz;
+	u32int	type;
+	char	*name;
+	uchar	*desc;
+	u32int	offset;	/* in-memory only */
+};
+
+struct ElfSym
+{
+	char*	name;
+	u32int	value;
+	u32int	size;
+	uchar	bind;
+	uchar	type;
+	uchar	other;
+	u16int	shndx;
+};
+
+struct Elf
+{
+	int		fd;
+	ElfHdr	hdr;
+	ElfSect	*sect;
+	uint		nsect;
+	ElfProg	*prog;
+	uint		nprog;
+	char		*shstrtab;
+
+	int		nsymtab;
+	ElfSect	*symtab;
+	ElfSect	*symstr;
+	int		ndynsym;
+	ElfSect	*dynsym;
+	ElfSect	*dynstr;
+	ElfSect	*bss;
+
+	int		(*coreregs)(Elf*, ElfNote*, uchar**);
+};
+
+Elf*	elfopen(char*);
+Elf*	elfinit(int);
+ElfSect *elfsection(Elf*, char*);
+void	elfclose(Elf*);
+int	elfsym(Elf*, int, ElfSym*);
+int	elfmap(Elf*, ElfSect*);
+
+int	coreregslinux386(Elf*, ElfNote*, uchar**);
+int	coreregsfreebsd386(Elf*, ElfNote*, uchar**);
blob - /dev/null
blob + f1e953fbef91237c7c92474c942456b24ac8259d (mode 644)
--- /dev/null
+++ src/libmach/elfcore.h
@@ -0,0 +1,142 @@
+/* Copyright (c) 2002, 2003 William Josephson */
+
+enum {
+	CoremapMagic	= 0xba5eba11,
+	CoremapMax	= 128,
+};
+#undef MAXCOMLEN
+#define MAXCOMLEN 16
+#define PRSTATUS_VERSION	1	/* Current version of prstatus_t */
+#define PRPSINFO_VERSION	1	/* Current version of prpsinfo_t */
+#define PRARGSZ			80	/* Maximum argument bytes saved */
+
+
+typedef struct Coremap Coremap;
+typedef struct CoremapItem CoremapItem;
+typedef struct CoremapHeader CoremapHeader;
+typedef struct ElfNote ElfNote;
+typedef struct Reg386 Reg386;
+typedef struct PrStatus386 PrStatus386;
+typedef struct PrPsinfo PrPsinfo;
+
+struct CoremapHeader {
+	u32int	magic;
+	u32int	counter;
+	u32int	maxelem;
+};
+
+struct CoremapItem {
+	u32int	address;
+	u32int	size;
+};
+
+struct Coremap {
+	CoremapHeader	header;
+	CoremapItem	map[CoremapMax];
+};
+
+struct ElfNote {
+	u32int	namesz;
+	u32int	descsz;
+	u32int	type;
+	char	*name;
+	uchar	*desc;
+	u32int	offset;	/* in-memory only */
+};
+
+enum
+{
+	NotePrStatus = 1,
+	NotePrFpreg = 2,
+	NotePrPsinfo = 3,
+	NotePrTaskstruct = 4,
+	NotePrAuxv = 6,
+	NotePrXfpreg = 0x46e62b7f,	/* according to gdb */
+};
+#if 0
+struct Reg386
+{
+	u32int	fs;
+	u32int	es;
+	u32int	ds;
+	u32int	edi;
+	u32int	esi;
+	u32int	ebp;
+	u32int	isp;
+	u32int	ebx;
+	u32int	edx;
+	u32int	ecx;
+	u32int	eax;
+	u32int	trapno;
+	u32int	err;
+	u32int	eip;
+	u32int	cs;
+	u32int	eflags;
+	u32int	esp;
+	u32int	ss;
+	u32int	gs;
+};
+#endif
+
+struct Reg386
+{
+	u32int	ebx;
+	u32int	ecx;
+	u32int	edx;
+	u32int	esi;
+	u32int	edi;
+	u32int	ebp;
+	u32int	eax;
+	u32int	ds;
+	u32int	es;
+	u32int	fs;
+	u32int	gs;
+	u32int	origeax;
+	u32int	eip;
+	u32int	cs;
+	u32int	eflags;
+	u32int	esp;
+	u32int	ss;
+};
+
+#if 0
+struct PrStatus386
+{
+    u32int		version;	/* Version number of struct (1) */
+    u32int		statussz;	/* sizeof(prstatus_t) (1) */
+    u32int		gregsetsz;	/* sizeof(gregset_t) (1) */
+    u32int		fpregsetsz;	/* sizeof(fpregset_t) (1) */
+    int			osreldate;	/* Kernel version (1) */
+    int			cursig;	/* Current signal (1) */
+    pid_t		pid;		/* Process ID (1) */
+    Reg386		reg;		/* General purpose registers (1) */
+};
+#endif
+
+struct PrPsinfo
+{
+    int		version;		/* Version number of struct (1) */
+    u32int	psinfosz;		/* sizeof(prpsinfo_t) (1) */
+    char	fname[MAXCOMLEN+1];	/* Command name, null terminated (1) */
+    char	psargs[PRARGSZ+1];	/* Arguments, null terminated (1) */
+};
+
+struct PrStatus386
+{
+	u32int	signo;
+	u32int	code;
+	u32int	errno;
+	u32int	cursig;
+	u32int	sigpend;
+	u32int	sighold;
+	u32int	pid;
+	u32int	ppid;
+	u32int	pgrp;
+	u32int	sid;
+	u32int	utime[2];
+	u32int	stime[2];
+	u32int	cutime[2];
+	u32int	cstime[2];
+	Reg386	reg;
+	u32int	fpvalid;
+};
blob - /dev/null
blob + 2ff746dee42843fa39c6604fb527172662975961 (mode 644)
--- /dev/null
+++ src/libmach/elfcorefreebsd386.c
@@ -0,0 +1,89 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "ureg386.h"
+
+typedef struct Lreg Lreg;
+typedef struct Status Status;
+
+struct Lreg
+{
+	u32int	fs;
+	u32int	es;
+	u32int	ds;
+	u32int	edi;
+	u32int	esi;
+	u32int	ebp;
+	u32int	isp;
+	u32int	ebx;
+	u32int	edx;
+	u32int	ecx;
+	u32int	eax;
+	u32int	trapno;
+	u32int	err;
+	u32int	eip;
+	u32int	cs;
+	u32int	eflags;
+	u32int	esp;
+	u32int	ss;
+	u32int	gs;
+};
+
+struct Status
+{
+    u32int		version;	/* Version number of struct (1) */
+    u32int		statussz;	/* sizeof(prstatus_t) (1) */
+    u32int		gregsetsz;	/* sizeof(gregset_t) (1) */
+    u32int		fpregsetsz;	/* sizeof(fpregset_t) (1) */
+    u32int		osreldate;	/* Kernel version (1) */
+    u32int		cursig;	/* Current signal (1) */
+    u32int		pid;		/* Process ID (1) */
+    Lreg		reg;		/* General purpose registers (1) */
+};
+
+int
+coreregsfreebsd386(Elf *elf, ElfNote *note, uchar **up)
+{
+	Status *s;
+	Lreg *l;
+	Ureg *u;
+
+	if(note->descsz < sizeof(Status)){
+		werrstr("elf status note too small");
+		return -1;
+	}
+	s = (Status*)note->desc;
+	if(s->version != 1){
+		werrstr("unknown status version %ud", (uint)s->version);
+		return -1;
+	}
+	l = &s->reg;
+	u = malloc(sizeof(Ureg));
+	if(u == nil)
+		return -1;
+
+	/* no byte order problems - just copying and rearranging */
+	u->di = l->edi;
+	u->si = l->esi;
+	u->bp = l->ebp;
+	u->nsp = l->esp;
+	u->bx = l->ebx;
+	u->dx = l->edx;
+	u->cx = l->ecx;
+	u->ax = l->eax;
+	u->gs = l->gs;
+	u->fs = l->fs;
+	u->es = l->es;
+	u->ds = l->ds;
+	u->trap = l->trapno;
+	u->ecode = l->err;
+	u->pc = l->eip;
+	u->cs = l->cs;
+	u->flags = l->eflags;
+	u->sp = l->esp;
+	u->ss = l->ss;
+	*up = (uchar*)u;
+	return sizeof(Ureg);
+}
+
blob - /dev/null
blob + f6a0234c64263db6aa4e2d6497d4b503a8a4c91b (mode 644)
--- /dev/null
+++ src/libmach/elfcorelinux386.c
@@ -0,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+#include "ureg386.h"
+
+typedef struct Lreg Lreg;
+typedef struct Status Status;
+
+struct Lreg
+{
+	u32int	ebx;
+	u32int	ecx;
+	u32int	edx;
+	u32int	esi;
+	u32int	edi;
+	u32int	ebp;
+	u32int	eax;
+	u32int	ds;
+	u32int	es;
+	u32int	fs;
+	u32int	gs;
+	u32int	origeax;
+	u32int	eip;
+	u32int	cs;
+	u32int	eflags;
+	u32int	esp;
+	u32int	ss;
+};
+
+/*
+ * Lreg is 64-bit aligned within status, so we shouldn't 
+ * have any packing problems. 
+ */
+struct Status
+{
+	u32int	signo;
+	u32int	code;
+	u32int	errno;
+	u32int	cursig;
+	u32int	sigpend;
+	u32int	sighold;
+	u32int	pid;
+	u32int	ppid;
+	u32int	pgrp;
+	u32int	sid;
+	u32int	utime[2];
+	u32int	stime[2];
+	u32int	cutime[2];
+	u32int	cstime[2];
+	Lreg	reg;
+	u32int	fpvalid;
+};
+
+int
+coreregslinux386(Elf *elf, ElfNote *note, uchar **up)
+{
+	Status *s;
+	Lreg *l;
+	Ureg *u;
+
+	if(note->descsz < sizeof(Status)){
+		werrstr("elf status note too small");
+		return -1;
+	}
+	s = (Status*)note->desc;
+	l = &s->reg;
+	u = malloc(sizeof(Ureg));
+	if(u == nil)
+		return -1;
+
+	/* no byte order problems - just copying and rearranging */
+	u->di = l->edi;
+	u->si = l->esi;
+	u->bp = l->ebp;
+	u->nsp = l->esp;
+	u->bx = l->ebx;
+	u->dx = l->edx;
+	u->cx = l->ecx;
+	u->ax = l->eax;
+	u->gs = l->gs;
+	u->fs = l->fs;
+	u->es = l->es;
+	u->ds = l->ds;
+	u->trap = ~0; // l->trapno;
+	u->ecode = ~0; // l->err;
+	u->pc = l->eip;
+	u->cs = l->cs;
+	u->flags = l->eflags;
+	u->sp = l->esp;
+	u->ss = l->ss;
+	*up = (uchar*)u;
+	return sizeof(Ureg);
+}
+
blob - /dev/null
blob + e96f818d310d032401f0dda129ba629db06ef884 (mode 644)
--- /dev/null
+++ src/libmach/elfdump.c
@@ -0,0 +1,133 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "elf.h"
+#include "stabs.h"
+
+void
+usage(void)
+{
+	fprint(2, "usage: elf file list\n");
+	fprint(2, "	elf file syms\n");
+	fprint(2, "	elf file prog n\n");
+	fprint(2, "	elf file sect n\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int i, n, nn;
+	char buf[512];
+	ulong off, len;
+	Elf *elf;
+	ElfProg *p;
+	ElfSect *s;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	if(argc < 2)
+		usage();
+
+	if((elf = elfopen(argv[0])) == nil)
+		sysfatal("elfopen %s: %r", argv[0]);
+
+	if(strcmp(argv[1], "syms") == 0){
+		ElfSym sym;
+		for(i=0; elfsym(elf, i, &sym) >= 0; i++){
+			print("%s 0x%lux +%lud bind %d type %d other %d shndx 0x%ux\n",
+				sym.name, (ulong)sym.value, (ulong)sym.size,
+				sym.bind, sym.type, sym.other, (uint)sym.shndx);
+		}
+	}
+	else if(strcmp(argv[1], "stabs") == 0){
+		ElfSect *s1, *s2;
+		Stab stabs;
+		StabSym sym;
+
+		if((s1 = elfsection(elf, ".stab")) == nil)
+			sysfatal("no stabs");
+		if(s1->link==0 || s1->link >= elf->nsect)
+			sysfatal("bad stabstr %d", s1->link);
+		s2 = &elf->sect[s1->link];
+		if(elfmap(elf, s1) < 0 || elfmap(elf, s2) < 0)
+			sysfatal("elfmap");
+		stabs.stabbase = s1->base;
+		stabs.stabsize = s1->size;
+		stabs.strbase = s2->base;
+		stabs.strsize = s2->size;
+		stabs.e2 = elf->hdr.e2;
+		stabs.e4 = elf->hdr.e4;
+		print("%ud %ud\n", stabs.stabsize, stabs.strsize);
+		for(i=0; stabsym(&stabs, i, &sym) >= 0; i++)
+			print("%s type 0x%x other %d desc %d value 0x%lux\n",
+				sym.name, sym.type, sym.other, (int)sym.desc, (ulong)sym.value);
+		fprint(2, "err at %d: %r\n", i);
+	}
+	else if(strcmp(argv[1], "list") == 0){
+		if(argc != 2)
+			usage();
+		print("elf %s %s v%d entry 0x%08lux phoff 0x%lux shoff 0x%lux flags 0x%lux\n",
+			elftype(elf->hdr.type), elfmachine(elf->hdr.machine),
+			elf->hdr.version, elf->hdr.entry, elf->hdr.phoff, elf->hdr.shoff,
+			elf->hdr.flags);
+		print("\tehsize %d phentsize %d phnum %d shentsize %d shnum %d shstrndx %d\n",
+			elf->hdr.ehsize, elf->hdr.phentsize, elf->hdr.phnum, elf->hdr.shentsize,
+			elf->hdr.shnum, elf->hdr.shstrndx);
+		for(i=0; i<elf->nprog; i++){
+			p = &elf->prog[i];
+			print("prog %d type %d offset 0x%08lux vaddr 0x%08lux paddr 0x%08lux filesz 0x%08lux memsz 0x%08lux flags 0x%08lux align 0x%08lux\n",
+				i, p->type, p->offset, p->vaddr, p->paddr,
+				p->filesz, p->memsz, p->flags, p->align);
+		}
+		for(i=0; i<elf->nsect; i++){
+			s = &elf->sect[i];
+			print("sect %d %s type %d flags 0x%lux addr 0x%08lux offset 0x%08lux size 0x%08lux link 0x%lux info 0x%lux align 0x%lux entsize 0x%lux\n",
+				i, s->name, s->type, s->flags, s->addr, s->offset, s->size, s->link, s->info,
+				s->align, s->entsize);
+		}
+	}
+	else if(strcmp(argv[1], "prog") == 0){
+		if(argc != 3)
+			usage();
+		i = atoi(argv[2]);
+		if(i < 0 || i >= elf->nprog)
+			sysfatal("bad prog number");
+		off = elf->prog[i].offset;
+		len = elf->prog[i].filesz;
+		fprint(2, "prog %d offset 0x%lux size 0x%lux\n", i, off, len);
+	copy:
+		seek(elf->fd, off, 0);
+		for(n=0; n<len; n+=nn){
+			nn = sizeof buf;
+			if(nn > len-n)
+				nn = len-n;
+			nn = read(elf->fd, buf, nn);
+			if(nn == 0)
+				break;
+			if(nn < 0)
+				sysfatal("read error");
+			write(1, buf, nn);
+		}
+		if(n < len)
+			fprint(2, "early eof\n");
+	}
+	else if(strcmp(argv[1], "sect") == 0){
+		if(argc != 3)
+			usage();
+		i = atoi(argv[2]);
+		if(i < 0 || i >= elf->nsect)
+			sysfatal("bad section number");
+		off = elf->sect[i].offset;
+		len = elf->sect[i].size;
+		fprint(2, "section %d offset 0x%lux size 0x%lux\n", i, off, len);
+		goto copy;
+	}
+	else
+		usage();
+	exits(0);
+}
blob - /dev/null
blob + 06bb41a63fb6cf85b97c58b0961068574d028d68 (mode 644)
--- /dev/null
+++ src/libmach/elfdynsym.c
@@ -0,0 +1,6 @@
+#include <u.h>
+#include <libc.h>
+#include "elf.h"
+
+int
+elfsym(Elf *elf, ElfSect *symtab, ElfSect *strtab, int ndx, ElfSym *
\ No newline at end of file
blob - /dev/null
blob + a94a096a95bccd29de1f9a79496fa3696ccd9466 (mode 644)
--- /dev/null
+++ src/libmach/fpformat.c
@@ -0,0 +1,65 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ *	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, Regdesc *rp, char *buf, uint n, uint modif)
+{
+	char reg[12];
+	u32int r;
+
+	switch(rp->format)
+	{
+	case 'X':
+		if (get4(map, rp->offset, &r) < 0)
+			return -1;
+		snprint(buf, n, "%lux", r);
+		break;
+	case 'F':	/* first reg of double reg pair */
+		if (modif == 'F')
+		if ((rp->format=='F') || (((rp+1)->flags&RFLT) && (rp+1)->format == 'f')) {
+			if (get1(map, rp->offset, (uchar *)reg, 8) < 0)
+				return -1;
+			mach->ftoa64(buf, n, reg);
+			if (rp->format == 'F')
+				return 1;
+			return 2;
+		}	
+			/* treat it like 'f' */
+		if (get1(map, rp->offset, (uchar *)reg, 4) < 0)
+			return -1;
+		mach->ftoa32(buf, n, reg);
+		break;
+	case 'f':	/* 32 bit float */
+		if (get1(map, rp->offset, (uchar *)reg, 4) < 0)
+			return -1;
+		mach->ftoa32(buf, n, reg);
+		break;
+	case '3':	/* little endian ieee 80 with hole in bytes 8&9 */
+		if (get1(map, rp->offset, (uchar *)reg, 10) < 0)
+			return -1;
+		memmove(reg+10, reg+8, 2);	/* open hole */
+		memset(reg+8, 0, 2);		/* fill it */
+		leieeeftoa80(buf, n, reg);
+		break;
+	case '8':	/* big-endian ieee 80 */
+		if (get1(map, rp->offset, (uchar *)reg, 10) < 0)
+			return -1;
+		beieeeftoa80(buf, n, reg);
+		break;
+	default:	/* unknown */
+		break;
+	}
+	return 1;
+}
blob - /dev/null
blob + 6e4485190b3a7eb03a4f5101a7c7870ce00dc7cc (mode 644)
--- /dev/null
+++ src/libmach/frame.c
@@ -0,0 +1,130 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+typedef struct LocRegs LocRegs;
+struct LocRegs
+{
+	Regs r;
+	Regs *oldregs;
+	Map *map;
+	ulong *val;
+};
+
+static int
+locregrw(Regs *regs, char *name, ulong *val, int isr)
+{
+	int i;
+	LocRegs *lr;
+
+	lr = (LocRegs*)regs;
+	i = windindex(name);
+	if(i == -1)
+		return lr->oldregs->rw(lr->oldregs, name, val, isr);
+	if(isr){
+		*val = lr->val[i];
+		return 0;
+	}else{
+		werrstr("saved registers are immutable");
+		return -1;
+	}
+}
+
+int
+stacktrace(Map *map, Regs *regs, Tracer trace)
+{
+	char *rname;
+	int i, ipc, ret;
+	ulong nextpc, pc, v;
+	ulong *cur, *next;
+	LocRegs lr;
+	Symbol s, *sp;
+
+	/*
+	 * Allocate location arrays.
+	 */
+	ret = -1;
+	cur = malloc(mach->nwindreg*sizeof(cur[0]));
+	next = malloc(mach->nwindreg*sizeof(cur[0]));
+	if(cur==nil || next==nil)
+		goto out;
+	
+	/*
+	 * Initialize current registers using regs.
+	 */
+	if(rget(regs, mach->pc, &pc) < 0){
+		werrstr("cannot fetch initial pc: %r");
+		goto out;	
+	}
+
+	for(i=0; i<mach->nwindreg; i++){
+		rname = mach->windreg[i];
+		if(rget(regs, rname, &v) < 0)
+			v = ~(ulong)0;
+		cur[i] = v;
+	}
+
+	ipc = windindex(mach->pc);
+	ret = 0;
+
+	/* set up cur[i]==next[i] for unwindframe */
+	memmove(next, cur, mach->nwindreg*sizeof(next[0]));
+	for(;;){
+		sp = &s;
+		if(findsym(locaddr(pc), CTEXT, &s) < 0)
+			sp = nil;
+
+		lr.r.rw = locregrw;
+		lr.oldregs = regs;
+		lr.val = cur;
+		lr.map = map;
+		if((i = unwindframe(map, &lr.r, next)) >= 0)
+			nextpc = next[ipc];
+		else
+			nextpc = ~(ulong)0;
+		if((*trace)(map, &lr.r, pc, nextpc, sp, ++ret) <= 0)
+			break;
+		if(i < 0)
+			break;
+		if(sp && strcmp(sp->name, "main") == 0)
+			break;
+		pc = nextpc;
+		memmove(cur, next, mach->nwindreg*sizeof(cur[0]));
+	}
+
+out:
+	free(cur);
+	free(next);
+	return ret;
+}
+
+int
+windindex(char *reg)
+{
+	char **p;
+	int i;
+
+	p = mach->windreg;
+	for(i=0; i<mach->nwindreg; i++)
+		if(strcmp(p[i], reg) == 0)
+			return i;
+	werrstr("%s is not a winding register", reg);
+	return -1;
+}
+
+Loc*
+windreglocs(void)
+{
+	int i;
+	Loc *loc;
+
+	loc = malloc(mach->nwindreg*sizeof(loc[0]));
+	if(loc == nil)
+		return nil;
+	for(i=0; i<mach->nwindreg; i++){
+		loc[i].type = LREG;
+		loc[i].reg = mach->windreg[i];
+	}
+	return loc;
+}
blob - /dev/null
blob + 5f3ed50e10689e14807f9372874b3712fe8f3d76 (mode 644)
--- /dev/null
+++ src/libmach/hexify.c
@@ -0,0 +1,20 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+char *
+_hexify(char *buf, ulong p, int zeros)
+{
+	ulong d;
+
+	d = p/16;
+	if(d)
+		buf = _hexify(buf, d, zeros-1);
+	else
+		while(zeros--)
+			*buf++ = '0';
+	*buf++ = "0123456789abcdef"[p&0x0f];
+	return buf;
+}
+
blob - /dev/null
blob + 0d756d212086412fa509a6a8250fe20ee3e23d06 (mode 644)
--- /dev/null
+++ src/libmach/ieee.c
@@ -0,0 +1,169 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * 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
+ieeeftoa64(char *buf, uint n, u32int h, u32int 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
+ieeeftoa32(char *buf, uint n, u32int 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
+beieeeftoa32(char *buf, uint n, void *s)
+{
+	return ieeeftoa32(buf, n, beswap4(*(u32int*)s));
+}
+
+int
+beieeeftoa64(char *buf, uint n, void *s)
+{
+	return ieeeftoa64(buf, n, beswap4(*(u32int*)s), beswap4(((u32int*)(s))[1]));
+}
+
+int
+leieeeftoa32(char *buf, uint n, void *s)
+{
+	return ieeeftoa32(buf, n, leswap4(*(u32int*)s));
+}
+
+int
+leieeeftoa64(char *buf, uint n, void *s)
+{
+	return ieeeftoa64(buf, n, leswap4(((u32int*)(s))[1]), leswap4(*(u32int*)s));
+}
+
+/* packed in 12 bytes, with s[2]==s[3]==0; mantissa starts at s[4]*/
+int
+beieeeftoa80(char *buf, uint n, void *s)
+{
+	uchar *reg = (uchar*)s;
+	int i;
+	ulong 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 beieeeftoa64(buf, n, (void*)ieee);
+}
+
+
+int
+leieeeftoa80(char *buf, uint n, void *s)
+{
+	int i;
+	char *cp;
+	char b[12];
+
+	cp = (char*) s;
+	for(i=0; i<12; i++)
+		b[11-i] = *cp++;
+	return beieeeftoa80(buf, n, b);
+}
blob - /dev/null
blob + 47bcf3d960f500f39bba14e203c452c89758df8e (mode 644)
--- /dev/null
+++ src/libmach/loc.c
@@ -0,0 +1,253 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+int
+locfmt(Fmt *fmt)
+{
+	Loc l;
+
+	l = va_arg(fmt->args, Loc);
+	switch(l.type){
+	default:
+		return fmtprint(fmt, "<loc%d>", l.type);
+	case LCONST:
+		return fmtprint(fmt, "0x%lux", l.addr);
+	case LADDR:
+		return fmtprint(fmt, "*0x%lux", l.addr);
+	case LOFFSET:
+		return fmtprint(fmt, "%ld(%s)", l.offset, l.reg);
+	case LREG:
+		return fmtprint(fmt, "%s", l.reg);
+	}
+}
+
+int
+loccmp(Loc *a, Loc *b)
+{
+	int i;
+
+	if(a->type < b->type)
+		return -1;
+	if(a->type > b->type)
+		return 1;
+	switch(a->type){
+	default:
+		return 0;
+	case LADDR:
+		if(a->addr < b->addr)
+			return -1;
+		if(a->addr > b->addr)
+			return 1;
+		return 0;
+	case LOFFSET:
+		i = strcmp(a->reg, b->reg);
+		if(i != 0)
+			return i;
+		if(a->offset < b->offset)
+			return -1;
+		if(a->offset > b->offset)
+			return 1;
+		return 0;
+	case LREG:
+		return strcmp(a->reg, b->reg);
+	}
+}
+
+int
+lget1(Map *map, Regs *regs, Loc loc, uchar *a, uint n)
+{
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return get1(map, loc.addr, a, n);
+	/* could do more here - i'm lazy */
+	werrstr("bad location for lget1");
+	return -1;
+}
+
+int
+lget2(Map *map, Regs *regs, Loc loc, u16int *u)
+{
+	ulong ul;
+
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return get2(map, loc.addr, u);
+	if(loc.type == LCONST){
+		*u = loc.addr;
+		return 0;
+	}
+	if(loc.type == LREG){
+		if(rget(regs, loc.reg, &ul) < 0)
+			return -1;
+		*u = ul;
+		return 0;
+	}
+	werrstr("bad location for lget2");
+	return -1;
+}
+
+int
+lget4(Map *map, Regs *regs, Loc loc, u32int *u)
+{
+	ulong ul;
+
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return get4(map, loc.addr, u);
+	if(loc.type == LCONST){
+		*u = loc.addr;
+		return 0;
+	}
+	if(loc.type == LREG){
+		if(rget(regs, loc.reg, &ul) < 0)
+			return -1;
+		*u = ul;
+		return 0;
+	}
+	werrstr("bad location for lget4");
+	return -1;
+}
+
+int
+lget8(Map *map, Regs *regs, Loc loc, u64int *u)
+{
+	ulong ul;
+
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return get8(map, loc.addr, u);
+	if(loc.type == LCONST){
+		*u = loc.addr;
+		return 0;
+	}
+	if(loc.type == LREG){
+		if(rget(regs, loc.reg, &ul) < 0)
+			return -1;
+		*u = ul;
+		return 0;
+	}
+	werrstr("bad location for lget8");
+	return -1;
+}
+
+int
+lput1(Map *map, Regs *regs, Loc loc, uchar *a, uint n)
+{
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return put1(map, loc.addr, a, n);
+	/* could do more here - i'm lazy */
+	werrstr("bad location for lput1");
+	return -1;
+}
+
+int
+lput2(Map *map, Regs *regs, Loc loc, u16int u)
+{
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return put2(map, loc.addr, u);
+	if(loc.type == LREG)
+		return rput(regs, loc.reg, u);
+	werrstr("bad location for lput2");
+	return -1;
+}
+
+int
+lput4(Map *map, Regs *regs, Loc loc, u32int u)
+{
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return put4(map, loc.addr, u);
+	if(loc.type == LREG)
+		return rput(regs, loc.reg, u);
+	werrstr("bad location for lput4");
+	return -1;
+}
+
+int
+lput8(Map *map, Regs *regs, Loc loc, u64int u)
+{
+	if(locsimplify(map, regs, loc, &loc) < 0)
+		return -1;
+	if(loc.type == LADDR)
+		return put8(map, loc.addr, u);
+	if(loc.type == LREG)
+		return rput(regs, loc.reg, u);
+	werrstr("bad location for lput8");
+	return -1;
+}
+
+Loc
+locaddr(ulong addr)
+{
+	Loc l;
+
+	l.type = LADDR;
+	l.addr = addr;
+	return l;
+}
+
+Loc
+locindir(char *reg, long offset)
+{
+	Loc l;
+
+	l.type = LOFFSET;
+	l.reg = reg;
+	l.offset = offset;
+	return l;
+}
+
+Loc
+locconst(ulong con)
+{
+	Loc l;
+
+	l.type = LCONST;
+	l.addr = con;
+	return l;
+}
+
+Loc
+locnone(void)
+{
+	Loc l;
+
+	l.type = LNONE;
+	return l;
+}
+
+Loc
+locreg(char *reg)
+{
+	Loc l;
+
+	l.type = LREG;
+	l.reg = reg;
+	return l;
+}
+
+int
+locsimplify(Map *map, Regs *regs, Loc loc, Loc *newloc)
+{
+	ulong u;
+
+	if(loc.type == LOFFSET){
+		if(rget(regs, loc.reg, &u) < 0)
+			return -1;
+		*newloc = locaddr(u + loc.offset);
+	}else
+		*newloc = loc;
+	return 0;
+}
+
blob - /dev/null
blob + 1b1dea73f7247ee862c96f650d397bc395779de9 (mode 644)
--- /dev/null
+++ src/libmach/localaddr.c
@@ -0,0 +1,56 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * XXX could remove the rock by hiding it in a special regs.
+ * That would still be sleazy but would be thread-safe.
+ */
+
+static struct {
+	int found;
+	int nframe;
+	Loc l;
+	char *fn;
+	char *var;
+} rock;
+
+static int
+ltrace(Map *map, Regs *regs, ulong pc, ulong nextpc, Symbol *sym, int depth)
+{
+	ulong v;
+	Symbol s1;
+
+	USED(pc);
+	USED(nextpc);
+	USED(depth);
+
+	if(sym==nil || strcmp(sym->name, rock.fn) != 0)
+		return ++rock.nframe < 40;
+	if(lookuplsym(sym, rock.var, &s1) < 0)
+		return 0;
+	if(locsimplify(map, regs, s1.loc, &rock.l) < 0)
+		return 0;
+	if(rock.l.type == LREG && rget(regs, rock.l.reg, &v) >= 0)
+		rock.l = locconst(v);
+	if(rock.l.type != LADDR && rock.l.type != LCONST)
+		return 0;
+	rock.found = 1;
+	return 0;
+}
+
+int
+localaddr(Map *map, Regs *regs, char *fn, char *var, ulong *val)
+{
+	rock.found = 0;
+	rock.nframe = 0;
+	rock.fn = fn;
+	rock.var = var;
+	stacktrace(map, regs, ltrace);
+	if(rock.found){
+		*val = rock.l.addr;
+		return 0;
+	}
+	return -1;
+}
blob - /dev/null
blob + 89a88f160d6e755b142c98f732b1ccea1d0cef7e (mode 644)
--- /dev/null
+++ src/libmach/mach.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+Mach *mach;
+
+extern Mach mach386;
+extern Mach machpower;
+
+static Mach *machs[] = 
+{
+	&mach386,
+	&machpower,
+};
+
+Mach*
+machbyname(char *name)
+{
+	int i;
+
+	for(i=0; i<nelem(machs); i++)
+		if(strcmp(machs[i]->name, name) == 0){
+			mach = machs[i];
+			return machs[i];
+		}
+	werrstr("machine '%s' not found", name);
+	return nil;
+}
+
blob - /dev/null
blob + 5f336ce7a0946991367a4a64695b5de8c4c27471 (mode 644)
--- /dev/null
+++ src/libmach/mach386.c
@@ -0,0 +1,1786 @@
+/*
+ * 386 definition
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "ureg386.h"
+
+#define	REGOFF(x)	(ulong)(&((struct Ureg *) 0)->x)
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FP_CTL(x)		(REGSIZE+4*(x))
+#define	FP_REG(x)		(FP_CTL(7)+10*(x))
+#define	FPREGSIZE	(6*4+8*10)
+
+/*
+ * i386-specific debugger interface
+ */
+
+static	char	*i386excep(Map*, Regs*);
+
+/*
+static	int	i386trace(Map*, ulong, ulong, ulong, Tracer);
+static	ulong	i386frame(Map*, ulong, ulong, ulong, ulong);
+*/
+static	int	i386foll(Map*, Regs*, ulong, ulong*);
+static	int	i386hexinst(Map*, ulong, char*, int);
+static	int	i386das(Map*, ulong, char, char*, int);
+static	int	i386instlen(Map*, ulong);
+static	char	*i386windregs[];
+static	int	i386unwind(Map*, Regs*, ulong*);
+
+static	Regdesc 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",		REGOFF(pc),		RINT, 'X'},
+	{"CS",		REGOFF(cs),	RINT, 'X'},
+	{"EFLAGS",	REGOFF(flags),	RINT, 'X'},
+	{"SP",		REGOFF(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(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 mach386 =
+{
+	"386",
+	M386,		/* 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 */
+	"BP",		/* name of FP */
+	0,		/* link register */
+	"setSB",	/* static base register name (bogus anyways) */
+	0,		/* static base register value */
+	0x1000,		/* page size */
+	0x80100000,	/* kernel base */
+	0,		/* kernel text mask */
+	1,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+
+	i386windregs,	/* locations unwound in stack trace */
+	9,
+
+	{0xCC, 0, 0, 0},	/* break point: INT 3 */
+	1,			/* break point size */
+
+	i386foll,		/* following addresses */
+	i386excep,		/* print exception */
+	i386unwind,		/* stack unwind */
+
+	leswap2,			/* convert short to local byte order */
+	leswap4,			/* convert long to local byte order */
+	leswap8,			/* convert vlong to local byte order */
+	leieeeftoa32,		/* single precision float pointer */
+	leieeeftoa64,		/* double precision float pointer */
+	leieeeftoa80,		/* long double precision floating point */
+
+	i386das,		/* dissembler */
+	i386das,		/* plan9-format disassembler */
+	0,			/* commercial disassembler */
+	i386hexinst,		/* print instruction */
+	i386instlen,		/* instruction size calculation */
+};
+
+static char *i386windregs[] = {
+	"PC",
+	"SP",
+	"BP",
+	"AX",
+	"CX",
+	"DX",
+	"BX",
+	"SI",
+	"DI",
+	0,
+};
+
+static int
+i386unwind(Map *map, Regs *regs, ulong *next)
+{
+	int isp, ipc, ibp;
+	ulong bp;
+	u32int v;
+
+	/* No symbol information, use frame pointer and do the best we can. */
+	isp = windindex("SP");
+	ipc = windindex("PC");
+	ibp = windindex("BP");
+	if(isp < 0 || ipc < 0 || ibp < 0){
+		werrstr("i386unwind: cannot happen");
+		return -1;
+	}
+
+	bp = next[ibp];
+
+	if(get4(map, bp, &v) < 0)
+		return -1;
+	next[ibp] = v;
+
+	next[isp] = bp+4;
+
+	if(get4(map, bp+4, &v) < 0)
+		return -1;
+	next[ipc] = v;
+
+	return 0;	
+}
+
+//static	char	STARTSYM[] =	"_main";
+//static	char	PROFSYM[] =	"_mainp";
+static	char	FRAMENAME[] =	".frame";
+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",
+[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",
+};
+
+static char*
+i386excep(Map *map, Regs *regs)
+{
+	ulong c;
+	ulong pc;
+	static char buf[16];
+
+	if(rget(regs, "TRAP", &c) < 0)
+		return "no trap register";
+
+	if(c > 64 || excname[c] == 0) {
+		if (c == 3) {
+			if (rget(regs, "PC", &pc) >= 0)
+			if (get1(map, pc, (uchar*)buf, mach->bpsize) > 0)
+			if (memcmp(buf, mach->bpinst, mach->bpsize) == 0)
+				return "breakpoint";
+		}
+		sprint(buf, "exception %ld", c);
+		return buf;
+	} else
+		return excname[c];
+}
+
+	/* 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 */
+	ulong	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 */
+	char	osize;		/* 'W' or 'L' */
+	char	asize;		/* address size 'W' or 'L' */
+	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 */
+	short	seg;		/* segment of far address */
+	ulong	disp;		/* displacement */
+	ulong 	imm;		/* immediate */
+	ulong 	imm2;		/* second immediate operand */
+	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,
+};
+	/* 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')
+%X	-	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
+%x	-	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)*/
+	Awd,			/* Address offset */
+	Iwds,			/* Operand-sized immediate (sign extended) */
+	RM,			/* Word or long R/M field with register (/r) */
+	RMB,			/* Byte R/M field with register (/r) */
+	RMOP,			/* Word or long 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 */
+	PRE,			/* Instr Prefix */
+	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 */
+};
+
+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 optab0F[256]=
+{
+[0x00]	RMOP,0,		optab0F00,
+[0x01]	RMOP,0,		optab0F01,
+[0x02]	RM,0,		"LAR	%e,%r",
+[0x03]	RM,0,		"LSL	%e,%r",
+[0x06]	0,0,		"CLTS",
+[0x08]	0,0,		"INVD",
+[0x09]	0,0,		"WBINVD",
+[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",
+[0x30]	0,0,		"WRMSR",
+[0x31]	0,0,		"RDTSC",
+[0x32]	0,0,		"RDMSR",
+[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 */
+[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",
+};
+
+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] =
+{
+[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]	AUX,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	%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,		"%X",			/* miserable CBW or CWDE */
+[0x99]	0,0,		"%x",			/* 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]	Iwd,0,		"MOV%S	%i,%OAX",
+[0xb9]	Iwd,0,		"MOV%S	%i,%OCX",
+[0xba]	Iwd,0,		"MOV%S	%i,%ODX",
+[0xbb]	Iwd,0,		"MOV%S	%i,%OBX",
+[0xbc]	Iwd,0,		"MOV%S	%i,%OSP",
+[0xbd]	Iwd,0,		"MOV%S	%i,%OBP",
+[0xbe]	Iwd,0,		"MOV%S	%i,%OSI",
+[0xbf]	Iwd,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]	PRE,0,		"REPNE",
+[0xf3]	PRE,0,		"REP",
+[0xf4]	0,0,		"HALT",
+[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,
+};
+
+/*
+ *  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, ulong *lp)
+{
+	ushort s;
+	long	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;
+}
+
+static int
+getdisp(Map *map, Instr *ip, int mod, int rm, int code)
+{
+	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;
+		} 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;
+	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);
+	}
+	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);
+	}
+	return getdisp(map, ip, mod, rm, 5);
+}
+
+static Optable *
+mkinstr(Map *map, Instr *ip, ulong pc)
+{
+	int i, n;
+	uchar c;
+	ushort s;
+	Optable *op, *obase;
+	char buf[128];
+
+	memset(ip, 0, sizeof(*ip));
+	ip->base = -1;
+	ip->index = -1;
+	if(0) /* asstype == AI8086) */
+		ip->osize = 'W';
+	else {
+		ip->osize = 'L';
+		ip->asize = 'E';
+	}
+	ip->addr = pc;
+	if (igetc(map, ip, &c) < 0)
+		return 0;
+	obase = optable;
+newop:
+	op = &obase[c];
+	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;
+			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->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;
+			break;
+		case Iw:	/* 16-bit immediate -> imm */
+			if (igets(map, ip, &s) < 0)
+				return 0;
+			ip->imm = s&0xffff;
+			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)*/
+			if (ip->osize == 'L') {
+				if (igetl(map, ip, &ip->imm) < 0)
+					return 0;
+			} 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;
+			} 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;
+			c = ip->reg;
+			obase = (Optable*)op->proto;
+			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 AUX:	/* Multi-byte op code - Auxiliary table */
+			obase = (Optable*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			goto newop;
+		case PRE:	/* Instr Prefix */
+			ip->prefix = (char*)op->proto;
+			if (igetc(map, ip, &c) < 0)
+				return 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->osize = 'W';
+			if (igetc(map, ip, &c) < 0)
+				return 0;
+			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;
+}
+
+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",
+};
+
+static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" };
+static char *sreg[] = { "ES", "CS", "SS", "DS", "FS", "GS" };
+
+static void
+plocal(Instr *ip)
+{
+	Symbol s;
+	char *name;
+	Loc l, li;
+
+	l.type = LOFFSET;
+	l.offset = ip->disp;
+	if(ip->base == SP)
+		l.reg = "SP";
+	else
+		l.reg = "BP";
+
+	li.type = LADDR;
+	li.addr = ip->addr;
+	if(findsym(li, CTEXT, &s) < 0)
+		goto raw;
+
+	name = nil;
+	if(ip->base==SP && lookuplsym(&s, FRAMENAME, &s) >= 0){
+		/* translate stack offset to offset from plan 9 frame pointer */
+		/* XXX not sure how to do this */
+	}
+
+	if(name==nil && findlsym(&s, l, &s) >= 0)
+		name = s.name;
+
+	if(name)
+		bprint(ip, "%s+", name);
+
+raw:
+	bprint(ip, "%lx(%s)", l.offset, l.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, long w, long val)
+{
+	Symbol next, tmp;
+	long 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((indexsym(s->index+1, &tmp) && loccmp(&tmp.loc, &s->loc)==0)
+			|| (indexsym(s->index-1, &tmp) && loccmp(&tmp.loc, &s->loc)==0))
+				*s = tmp;
+		}
+		if (w == 0)
+			return 1;
+		for (next=*s; next.loc.addr==s->loc.addr; next=tmp)
+			if (!indexsym(next.index+1, &tmp))
+				break;
+		size = next.loc.addr - s->loc.addr;
+		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, long val)
+{
+	Symbol s;
+	long w;
+	Loc l;
+
+	l.type = LADDR;
+	l.addr = val;
+	if (findsym(l, CANY, &s) >= 0) {
+		w = val - s.loc.addr;
+		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 && indexsym(s.index+1, &s) >= 0) {
+			w = s.loc.addr - val;
+			if (w < 0)
+				w = -w;
+			if (w < 4096) {
+				bprint(ip, "%s-%lux(SB)", s.name, w);
+				return;
+			}
+		}
+	}
+	bprint(ip, "%lux", val);
+}
+
+static void
+pea(Instr *ip)
+{
+	if (ip->mod == 3) {
+		if (ip->osize == 'B')
+			bprint(ip, breg[(uchar)ip->base]);
+		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 || ip->base == BP))
+		plocal(ip);
+	else {
+		if (ip->base < 0)
+			immediate(ip, ip->disp);
+		else
+			bprint(ip,"(%s%s)", ANAME(ip), reg[(uchar)ip->base]);
+	}
+	if (ip->index >= 0)
+		bprint(ip,"(%s%s*%d)", ANAME(ip), reg[(uchar)ip->index], 1<<ip->ss);
+}
+
+static void
+prinstr(Instr *ip, char *fmt)
+{
+	if (ip->prefix)
+		bprint(ip, "%s ", ip->prefix);
+	for (; *fmt && ip->curr < ip->end; fmt++) {
+		if (*fmt != '%')
+			*ip->curr++ = *fmt;
+		else 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, "$");
+			immediate(ip,ip->imm);
+			break;
+		case 'R':
+			bprint(ip, "%s%s", ONAME(ip), reg[ip->reg]);
+			break;
+		case 'S':
+			bprint(ip, "%c", ip->osize);
+			break;
+		case 'T':
+			if (ip->reg == 6 || ip->reg == 7)
+				bprint(ip, "TR%d",ip->reg);
+			else
+				bprint(ip, "???");
+			break;
+		case 'X':
+			if (ip->osize == 'L')
+				bprint(ip,"CWDE");
+			else
+				bprint(ip, "CBW");
+			break;
+		case 'd':
+			bprint(ip,"%lux:%lux",ip->seg,ip->disp);
+			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':
+			immediate(ip, ip->imm+ip->addr+ip->n);
+			break;
+		case 'r':
+			if (ip->osize == 'B')
+				bprint(ip,"%s",breg[ip->reg]);
+			else
+				bprint(ip, reg[ip->reg]);
+			break;
+		case 'x':
+			if (ip->osize == 'L')
+				bprint(ip,"CDQ");
+			else
+				bprint(ip, "CWD");
+			break;
+		default:
+			bprint(ip, "%%%c", *fmt);
+			break;
+		}
+	}
+	*ip->curr = 0;		/* there's always room for 1 byte */
+}
+
+static int
+i386das(Map *map, ulong 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
+i386hexinst(Map *map, ulong 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, ulong pc)
+{
+	Instr i;
+
+	if (mkinstr(map, &i, pc))
+		return i.n;
+	return -1;
+}
+
+static int
+i386foll(Map *map, Regs *regs, ulong pc, ulong *foll)
+{
+	Instr i;
+	Optable *op;
+	ushort s;
+	ulong addr;
+	u32int l;
+	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 (lget4(map, regs, locindir("BP", 0), &l) < 0)
+				return -1;
+		} else if (lget4(map, regs, locindir(mach->sp, 0), &l) < 0)
+			return -1;
+		foll[0] = l;
+		return 1;
+	case Iwds:		/* pc relative JUMP or CALL*/
+	case Jbs:		/* pc relative JUMP or CALL */
+		foll[0] = pc+i.imm+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) {
+			if (rget(regs, reg[(uchar)i.base], &foll[0]) < 0)
+				return -1;
+			return 1;
+		}
+			/* calculate the effective address */
+		addr = i.disp;
+		if (i.base >= 0) {
+			if (lget4(map, regs, locindir(reg[(uchar)i.base], 0), &l) < 0)
+				return -1;
+			addr += l;
+		}
+		if (i.index >= 0) {
+			if (lget4(map, regs, locindir(reg[(uchar)i.index], 0), &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 (get4(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;
+}
blob - /dev/null
blob + 641db598c9732ed2875ce2bc076c117929e9bc37 (mode 644)
--- /dev/null
+++ src/libmach/macho.c
@@ -0,0 +1,128 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+
+/*
+http://www.channelu.com/NeXT/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/
+*/
+
+Macho*
+machoopen(char *name)
+{
+	int fd;
+	Macho *m;
+
+	if((fd = open(name, OREAD)) < 0)
+		return nil;
+	m = machoinit(fd);
+	if(m == nil)
+		close(fd);
+	return m;
+}
+
+static int
+unpackseg(uchar *p, Macho *m, MachoCmd *c, uint type, uint sz)
+{
+	u32int (*e4)(uchar*);
+
+	e4 = m->e4;
+
+	c->type = type;
+	c->size = sz;
+	switch(type){
+	default:
+		return -1;
+	case MachoCmdSegment:
+		if(sz < 56)
+			return -1;
+		strecpy(c->seg.name, c->seg.name+sizeof c->seg.name, (char*)p+8);
+		c->seg.vmaddr = e4(p+24);
+		c->seg.vmsize = e4(p+28);
+		c->seg.fileoff = e4(p+32);
+		c->seg.filesz = e4(p+36);
+		c->seg.maxprot = e4(p+40);
+		c->seg.initprot = e4(p+44);
+		c->seg.nsect = e4(p+48);
+		c->seg.flags = e4(p+52);
+		break;
+	case MachoCmdSymtab:
+		if(sz < 24)
+			return -1;
+		c->sym.symoff = e4(p+8);
+		c->sym.nsyms = e4(p+12);
+		c->sym.stroff = e4(p+16);
+		c->sym.strsize = e4(p+20);
+		break;
+	}
+	return 0;
+}
+
+
+Macho*
+machoinit(int fd)
+{
+	int i;
+	uchar hdr[7*4], *cmdp;
+	u32int (*e4)(uchar*);
+	ulong ncmd, cmdsz, ty, sz, off;
+	Macho *m;
+
+	if(seek(fd, 0, 0) < 0 || readn(fd, hdr, sizeof hdr) != sizeof hdr)
+		return nil;
+
+	if(beload4(hdr) == 0xFEEDFACE)
+		e4 = beload4;
+	else if(leload4(hdr) == 0xFEEDFACE)
+		e4 = leload4;
+	else{
+		werrstr("bad magic - not mach-o file");
+		return nil;
+	}
+
+	ncmd = e4(hdr+4*4);
+	cmdsz = e4(hdr+5*4);
+	if(ncmd > 0x10000 || cmdsz >= 0x01000000){
+		werrstr("implausible mach-o header ncmd=%lud cmdsz=%lud", ncmd, cmdsz);
+		return nil;
+	}
+
+	m = mallocz(sizeof(*m)+ncmd*sizeof(MachoCmd)+cmdsz, 1);
+	if(m == nil)
+		return nil;
+
+	m->fd = fd;
+	m->e4 = e4;
+	m->cputype = e4(hdr+1*4);
+	m->subcputype = e4(hdr+2*4);
+	m->filetype = e4(hdr+3*4);
+	m->ncmd = ncmd;
+	m->flags = e4(hdr+6*4);
+
+	m->cmd = (MachoCmd*)(m+1);
+	off = sizeof hdr;
+	cmdp = (uchar*)(m->cmd+ncmd);
+	if(readn(fd, cmdp, cmdsz) != cmdsz){
+		werrstr("reading cmds: %r");
+		free(m);
+		return nil;
+	}
+
+	for(i=0; i<ncmd; i++){
+		ty = e4(cmdp);
+		sz = e4(cmdp+4);
+		m->cmd[i].off = off;
+		unpackseg(cmdp, m, &m->cmd[i], ty, sz);
+		cmdp += sz;
+		off += sz;
+	}
+
+	return m;
+}
+
+void
+machoclose(Macho *m)
+{
+	close(m->fd);
+	free(m);
+}
blob - /dev/null
blob + 377e0d2b85dacc16d26e501ab6b5c3da4edf0207 (mode 644)
--- /dev/null
+++ src/libmach/macho.h
@@ -0,0 +1,71 @@
+typedef struct Macho Macho;
+typedef struct MachoCmd MachoCmd;
+
+enum
+{
+	MachoCpuVax = 1,
+	MachoCpu68000 = 6,
+	MachoCpu386 = 7,
+	MachoCpuMips = 8,
+	MachoCpu98000 = 10,
+	MachoCpuHppa = 11,
+	MachoCpuArm = 12,
+	MachoCpu88000 = 13,
+	MachoCpuSparc = 14,
+	MachoCpu860 = 15,
+	MachoCpuAlpha = 16,
+	MachoCpuPower = 18,
+
+	MachoCmdSegment = 1,
+	MachoCmdSymtab = 2,
+	MachoCmdSymseg = 3,
+	MachoCmdThread = 4,
+
+	MachoFileObject = 1,
+	MachoFileExecutable = 2,
+	MachoFileFvmlib = 3,
+	MachoFileCore = 4,
+	MachoFilePreload = 5,
+};
+
+struct MachoCmd
+{
+	int type;
+	ulong off;
+	ulong size;
+	struct {
+		char name[16+1];
+		ulong vmaddr;
+		ulong vmsize;
+		ulong fileoff;
+		ulong filesz;
+		ulong maxprot;
+		ulong initprot;
+		ulong nsect;
+		ulong flags;
+	} seg;
+	struct {
+		ulong symoff;
+		ulong nsyms;
+		ulong stroff;
+		ulong strsize;
+	} sym;
+};
+
+struct Macho
+{
+	int fd;
+	uint cputype;
+	uint subcputype;
+	ulong filetype;
+	ulong flags;
+	MachoCmd *cmd;
+	uint ncmd;
+	u32int (*e4)(uchar*);
+	int (*coreregs)(Macho*, uchar**);
+};
+
+Macho *machoopen(char*);
+Macho *machoinit(int);
+void machoclose(Macho*);
+int coreregsmachopower(Macho*, uchar**);
blob - /dev/null
blob + 0eb3761d18d65922d4d8510bc7024ff93db31016 (mode 644)
--- /dev/null
+++ src/libmach/machocorepower.c
@@ -0,0 +1,173 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+#include "uregpower.h"
+
+enum
+{
+	ThreadState = 1,
+	FloatState,
+	ExceptionState,
+	VectorState,
+	ThreadState64,
+	ExceptionState64,
+	ThreadStateNone,
+};
+
+typedef struct Lreg Lreg;
+typedef struct Lflt Lflt;
+typedef struct Lexc Lexc;
+
+struct Lreg
+{
+	u32int srr0;
+	u32int srr1;
+	u32int r0;
+	u32int r1;
+	u32int r2;
+	u32int r3;
+	u32int r4;
+	u32int r5;
+	u32int r6;
+	u32int r7;
+	u32int r8;
+	u32int r9;
+	u32int r10;
+	u32int r11;
+	u32int r12;
+	u32int r13;
+	u32int r14;
+	u32int r15;
+	u32int r16;
+	u32int r17;
+	u32int r18;
+	u32int r19;
+	u32int r20;
+	u32int r21;
+	u32int r22;
+	u32int r23;
+	u32int r24;
+	u32int r25;
+	u32int r26;
+	u32int r27;
+	u32int r28;
+	u32int r29;
+	u32int r30;
+	u32int r31;
+
+	u32int cr;
+	u32int xer;
+	u32int lr;
+	u32int ctr;
+	u32int mq;
+
+	u32int vrsave;
+};
+
+struct Lflt
+{
+	u32int fpregs[32*2];		/* 32 doubles */
+	u32int fpscr[2];
+
+};
+
+struct Lexc
+{
+	u32int dar;
+	u32int dsisr;
+	u32int exception;
+	u32int pad0;
+	u32int pad1[4];
+};
+
+static void
+lreg2ureg(Lreg *l, Ureg *u)
+{
+	u->pc = l->srr0;
+	u->srr1 = l->srr1;
+	u->lr = l->lr;
+	u->cr = l->cr;
+	u->xer = l->xer;
+	u->ctr = l->ctr;
+	u->vrsave = l->vrsave;
+	memmove(&u->r0, &l->r0, 32*4);
+}
+
+static void
+lexc2ureg(Lexc *l, Ureg *u)
+{
+	u->cause = l->exception;
+	u->dar = l->dar;
+	u->dsisr = l->dsisr;
+}
+
+static uchar*
+load(int fd, ulong off, int size)
+{
+	uchar *a;
+
+	a = malloc(size);
+	if(a == nil)
+		return nil;
+	if(seek(fd, off, 0) < 0 || readn(fd, a, size) != size){
+		free(a);
+		return nil;
+	}
+	return a;
+}
+
+int
+coreregsmachopower(Macho *m, uchar **up)
+{
+	int i, havereg, haveexc;
+	uchar *a, *p, *nextp;
+	Ureg *u;
+	ulong flavor, count;
+	MachoCmd *c;
+
+	*up = nil;
+	for(i=0; i<m->ncmd; i++)
+		if(m->cmd[i].type == MachoCmdThread)
+			break;
+	if(i == m->ncmd){
+		werrstr("no registers found");
+		return -1;
+	}
+
+	c = &m->cmd[i];
+	a = load(m->fd, c->off, c->size);
+	if(a == nil)
+		return -1;
+
+	if((u = mallocz(sizeof(Ureg), 1)) == nil){
+		free(a);
+		return -1;
+	}
+
+	havereg = haveexc = 0;
+	for(p=a+8; p<a+c->size; p=nextp){
+		flavor = m->e4(p);
+		count = m->e4(p+4);
+		nextp = p+8+count*4;
+		if(flavor == ThreadState && count*4 == sizeof(Lreg)){
+			havereg = 1;
+			lreg2ureg((Lreg*)(p+8), u);
+		}
+		if(flavor == ExceptionState && count*4 == sizeof(Lexc)){
+			haveexc = 1;
+			lexc2ureg((Lexc*)(p+8), u);
+		}
+	}
+	free(a);
+	if(!havereg){
+		werrstr("no registers found");
+		free(u);
+		return -1;
+	}
+	if(!haveexc)
+		fprint(2, "warning: no exception state in core file registers\n");
+	*up = (uchar*)u;
+	return sizeof(*u);
+}
+
blob - /dev/null
blob + 181061aa7d7d269986d25f4058584e1e42d835df (mode 644)
--- /dev/null
+++ src/libmach/machodump.c
@@ -0,0 +1,93 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "stabs.h"
+#include "macho.h"
+
+void
+usage(void)
+{
+	fprint(2, "usage: machodump file list\n");
+	fprint(2, "	machodump file stabs\n");
+	exits("usage");
+}
+
+uchar*
+load(int fd, ulong off, int size)
+{
+	uchar *a;
+
+	a = malloc(size);
+print("malloc %d -> %p\n", size, a);
+	if(a == nil)
+		sysfatal("malloc: %r");
+	if(seek(fd, off, 0) < 0)
+		sysfatal("seek %lud: %r", off);
+	if(readn(fd, a, size) != size)
+		sysfatal("readn: %r");
+	return a;
+}
+
+void
+main(int argc, char **argv)
+{
+	int i;
+	Macho *m;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	if(argc < 2)
+		usage();
+
+	if((m = machoopen(argv[0])) == nil)
+		sysfatal("machoopen %s: %r", argv[0]);
+
+	if(strcmp(argv[1], "stabs") == 0){
+		Stab stabs;
+		StabSym sym;
+
+		for(i=0; i<m->ncmd; i++){
+			if(m->cmd[i].type == MachoCmdSymtab){
+				stabs.stabbase = load(m->fd, m->cmd[i].sym.symoff, m->cmd[i].sym.nsyms*16);
+				stabs.stabsize = m->cmd[i].sym.nsyms*16;
+				stabs.strbase = load(m->fd, m->cmd[i].sym.stroff, m->cmd[i].sym.strsize);
+				stabs.strsize = m->cmd[i].sym.strsize;
+				stabs.e4 = m->e4;
+				stabs.e2 = (m->e4 == beload4 ? beload2 : leload2);
+				print("cmd%d: %p %ud %p %ud\n", i, stabs.stabbase, stabs.stabsize, stabs.strbase, stabs.strsize);
+				for(i=0; stabsym(&stabs, i, &sym) >= 0; i++)
+					print("%s type 0x%x other %d desc %d value 0x%lux\n",
+						sym.name, sym.type, sym.other, (int)sym.desc, (ulong)sym.value);
+				print("err at %d: %r\n", i);
+			}
+		}
+	}
+	else if(strcmp(argv[1], "list") == 0){
+		print("macho cpu %ud sub %ud filetype %lud flags %lud\n",
+			m->cputype, m->subcputype, m->filetype, m->flags);
+		for(i=0; i<m->ncmd; i++){
+			switch(m->cmd[i].type){
+			case MachoCmdSymtab:
+				print("cmd%d: symtab %lud+%lud %lud+%lud\n", i,
+					m->cmd[i].sym.symoff, m->cmd[i].sym.nsyms,
+					m->cmd[i].sym.stroff, m->cmd[i].sym.strsize);
+				break;
+			case MachoCmdSegment:
+				print("cmd%d: segment %s vm 0x%lux+0x%lux file 0x%lux+0x%lux prot 0x%lux/0x%lux ns %d flags 0x%lux\n", i,
+					m->cmd[i].seg.name, m->cmd[i].seg.vmaddr, m->cmd[i].seg.vmsize,
+					m->cmd[i].seg.fileoff, m->cmd[i].seg.filesz, m->cmd[i].seg.maxprot,
+					m->cmd[i].seg.initprot, m->cmd[i].seg.nsect, m->cmd[i].seg.flags);
+				break;
+			default:
+				print("cmd%d: type %d offset %lud\n", i, m->cmd[i].type, m->cmd[i].off);
+				break;
+			}
+		}
+	}
+	else
+		usage();
+	exits(0);
+}
blob - /dev/null
blob + d223f4f56546b2a9cf169e8a6c5f8824e1e13d42 (mode 644)
--- /dev/null
+++ src/libmach/machpower.c
@@ -0,0 +1,1409 @@
+/*
+ * PowerPC definition
+ *	forsyth@plan9.cs.york.ac.uk
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "uregpower.h"
+#include <mach.h>
+
+/*
+ * PowerPC-specific debugger interface
+ *	forsyth@plan9.cs.york.ac.uk
+ */
+
+static	char	*powerexcep(Map*, Regs*);
+static	int	powerfoll(Map*, Regs*, ulong, ulong*);
+static	int	powerdas(Map*, ulong, char, char*, int);
+static	int	powerinstlen(Map*, ulong);
+static	int	powerhexinst(Map*, ulong, char*, int);
+
+static char *excname[] =
+{
+	"reserved 0",
+	"system reset",
+	"machine check",
+	"data access",
+	"instruction access",
+	"external interrupt",
+	"alignment",
+	"program exception",
+	"floating-point unavailable",
+	"decrementer",
+	"i/o controller interface error",
+	"reserved B",
+	"system call",
+	"trace trap",
+	"floating point assist",
+	"reserved",
+	"ITLB miss",
+	"DTLB load miss",
+	"DTLB store miss",
+	"instruction address breakpoint"
+	"SMI interrupt"
+	"reserved 15",
+	"reserved 16",
+	"reserved 17",
+	"reserved 18",
+	"reserved 19",
+	"reserved 1A",
+	/* the following are made up on a program exception */
+	"floating point exception",		/* FPEXC */
+	"illegal instruction",
+	"privileged instruction",
+	"trap",
+	"illegal operation",
+};
+
+static char*
+powerexcep(Map *map, Regs *regs)
+{
+	ulong c;
+	static char buf[32];
+
+	if(rget(regs, "CAUSE", &c) < 0)
+		return "no cause register";
+	c >>= 8;
+	if(c < nelem(excname))
+		return excname[c];
+	sprint(buf, "unknown trap #%lux", c);
+	return buf;
+}
+
+/*
+ * disassemble PowerPC opcodes
+ */
+
+#define	REGSP	1	/* should come from q.out.h, but there's a clash */
+#define	REGSB	2
+
+//static	char FRAMENAME[] = ".frame";
+
+static Map *mymap;
+
+/*
+ * ibm conventions for these: bit 0 is top bit
+ *	from table 10-1
+ */
+typedef struct {
+	uchar	aa;		/* bit 30 */
+	uchar	crba;		/* bits 11-15 */
+	uchar	crbb;		/* bits 16-20 */
+	long	bd;		/* bits 16-29 */
+	uchar	crfd;		/* bits 6-8 */
+	uchar	crfs;		/* bits 11-13 */
+	uchar	bi;		/* bits 11-15 */
+	uchar	bo;		/* bits 6-10 */
+	uchar	crbd;		/* bits 6-10 */
+	union {
+		short	d;	/* bits 16-31 */
+		short	simm;
+		ushort	uimm;
+	};
+	uchar	fm;		/* bits 7-14 */
+	uchar	fra;		/* bits 11-15 */
+	uchar	frb;		/* bits 16-20 */
+	uchar	frc;		/* bits 21-25 */
+	uchar	frs;		/* bits 6-10 */
+	uchar	frd;		/* bits 6-10 */
+	uchar	crm;		/* bits 12-19 */
+	long	li;		/* bits 6-29 || b'00' */
+	uchar	lk;		/* bit 31 */
+	uchar	mb;		/* bits 21-25 */
+	uchar	me;		/* bits 26-30 */
+	uchar	nb;		/* bits 16-20 */
+	uchar	op;		/* bits 0-5 */
+	uchar	oe;		/* bit 21 */
+	uchar	ra;		/* bits 11-15 */
+	uchar	rb;		/* bits 16-20 */
+	uchar	rc;		/* bit 31 */
+	union {
+		uchar	rs;	/* bits 6-10 */
+		uchar	rd;
+	};
+	uchar	sh;		/* bits 16-20 */
+	ushort	spr;		/* bits 11-20 */
+	uchar	to;		/* bits 6-10 */
+	uchar	imm;		/* bits 16-19 */
+	ushort	xo;		/* bits 21-30, 22-30, 26-30, or 30 (beware) */
+	long	immediate;
+	long w0;
+	long w1;
+	ulong	addr;		/* pc of instruction */
+	short	target;
+	char	*curr;		/* current fill level in output buffer */
+	char	*end;		/* end of buffer */
+	int 	size;		/* number of longs in instr */
+	char	*err;		/* errmsg */
+} Instr;
+
+#define	IBF(v,a,b) (((ulong)(v)>>(32-(b)-1)) & ~(~0L<<(((b)-(a)+1))))
+#define	IB(v,b) IBF((v),(b),(b))
+
+static void
+bprint(Instr *i, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	i->curr = vseprint(i->curr, i->end, fmt, arg);
+	va_end(arg);
+}
+
+static int
+decode(ulong pc, Instr *i)
+{
+	u32int w;
+
+	if (get4(mymap, pc, &w) < 0) {
+		werrstr("can't read instruction: %r");
+		return -1;
+	}
+	i->aa = IB(w, 30);
+	i->crba = IBF(w, 11, 15);
+	i->crbb = IBF(w, 16, 20);
+	i->bd = IBF(w, 16, 29)<<2;
+	if(i->bd & 0x8000)
+		i->bd |= ~0L<<16;
+	i->crfd = IBF(w, 6, 8);
+	i->crfs = IBF(w, 11, 13);
+	i->bi = IBF(w, 11, 15);
+	i->bo = IBF(w, 6, 10);
+	i->crbd = IBF(w, 6, 10);
+	i->uimm = IBF(w, 16, 31);	/* also d, simm */
+	i->fm = IBF(w, 7, 14);
+	i->fra = IBF(w, 11, 15);
+	i->frb = IBF(w, 16, 20);
+	i->frc = IBF(w, 21, 25);
+	i->frs = IBF(w, 6, 10);
+	i->frd = IBF(w, 6, 10);
+	i->crm = IBF(w, 12, 19);
+	i->li = IBF(w, 6, 29)<<2;
+	if(IB(w, 6))
+		i->li |= ~0<<25;
+	i->lk = IB(w, 31);
+	i->mb = IBF(w, 21, 25);
+	i->me = IBF(w, 26, 30);
+	i->nb = IBF(w, 16, 20);
+	i->op = IBF(w, 0, 5);
+	i->oe = IB(w, 21);
+	i->ra = IBF(w, 11, 15);
+	i->rb = IBF(w, 16, 20);
+	i->rc = IB(w, 31);
+	i->rs = IBF(w, 6, 10);	/* also rd */
+	i->sh = IBF(w, 16, 20);
+	i->spr = IBF(w, 11, 20);
+	i->to = IBF(w, 6, 10);
+	i->imm = IBF(w, 16, 19);
+	i->xo = IBF(w, 21, 30);		/* bits 21-30, 22-30, 26-30, or 30 (beware) */
+	i->immediate = i->simm;
+	if(i->op == 15)
+		i->immediate <<= 16;
+	i->w0 = w;
+	i->target = -1;
+	i->addr = pc;
+	i->size = 1;
+	return 1;
+}
+
+static int
+mkinstr(ulong pc, Instr *i)
+{
+	Instr x;
+
+	if(decode(pc, i) < 0)
+		return -1;
+	/*
+	 * combine ADDIS/ORI (CAU/ORIL) into MOVW
+	 */
+	if (i->op == 15 && i->ra==0) {
+		if(decode(pc+4, &x) < 0)
+			return -1;
+		if (x.op == 24 && x.rs == x.ra && x.ra == i->rd) {
+			i->immediate |= (x.immediate & 0xFFFF);
+			i->w1 = x.w0;
+			i->target = x.rd;
+			i->size++;
+			return 1;
+		}
+	}
+	return 1;
+}
+
+static int
+plocal(Instr *i)
+{
+	Symbol s;
+	Loc l, li;
+
+	l.type = LOFFSET;
+	l.offset = i->immediate;
+	l.reg = "SP";
+
+	li.type = LADDR;
+	li.addr = i->addr;
+	if (findsym(li, CTEXT, &s)<0 || findlsym(&s, l, &s)<0)
+		return -1;
+	bprint(i, "%s%+ld(SP)", s.name, (long)i->immediate);
+	return 0;
+}
+
+static int
+pglobal(Instr *i, long off, int anyoff, char *reg)
+{
+	Symbol s, s2;
+	u32int off1;
+	Loc l;
+
+	l.type = LADDR;
+	l.addr = off;
+	if(findsym(l, CANY, &s)>=0 && s.loc.type==LADDR &&
+	   s.loc.addr-off < 4096 &&
+	   (s.class == CDATA || s.class == CTEXT)) {
+		if(off==s.loc.addr && s.name[0]=='$'){
+			off1 = 0;
+			get4(mymap, s.loc.addr, &off1);
+			l.addr = off1;
+			if(off1 && findsym(l, CANY, &s2)>=0 && s2.loc.type==LADDR && s2.loc.addr == off1){
+				bprint(i, "$%s%s", s2.name, reg);
+				return 1;
+			}
+		}
+		bprint(i, "%s", s.name);
+		if (s.loc.addr != off)
+			bprint(i, "+%lux", off-s.loc.addr);
+		bprint(i, reg);
+		return 1;
+	}
+	if(!anyoff)
+		return 0;
+	bprint(i, "%lux%s", off, reg);
+	return 1;
+}
+
+static void
+address(Instr *i)
+{
+	if (i->ra == REGSP && plocal(i) >= 0)
+		return;
+	if (i->ra == REGSB && mach->sb && pglobal(i, mach->sb+i->immediate, 0, "(SB)") >= 0)
+		return;
+	if(i->simm < 0)
+		bprint(i, "-%lx(R%d)", -i->simm, i->ra);
+	else
+		bprint(i, "%lux(R%d)", i->immediate, i->ra);
+}
+
+static	char	*tcrbits[] = {"LT", "GT", "EQ", "VS"};
+static	char	*fcrbits[] = {"GE", "LE", "NE", "VC"};
+
+typedef struct Opcode Opcode;
+
+struct Opcode {
+	uchar	op;
+	ushort	xo;
+	ushort	xomask;
+	char	*mnemonic;
+	void	(*f)(Opcode *, Instr *);
+	char	*ken;
+	int	flags;
+};
+
+static void format(char *, Instr *, char *);
+
+static void
+branch(Opcode *o, Instr *i)
+{
+	char buf[8];
+	int bo, bi;
+
+	bo = i->bo & ~1;	/* ignore prediction bit */
+	if(bo==4 || bo==12 || bo==20) {	/* simple forms */
+		if(bo != 20) {
+			bi = i->bi&3;
+			sprint(buf, "B%s%%L", bo==12? tcrbits[bi]: fcrbits[bi]);
+			format(buf, i, 0);
+			bprint(i, "\t");
+			if(i->bi > 4)
+				bprint(i, "CR(%d),", i->bi/4);
+		} else
+			format("BR%L\t", i, 0);
+		if(i->op == 16)
+			format(0, i, "%J");
+		else if(i->op == 19 && i->xo == 528)
+			format(0, i, "(CTR)");
+		else if(i->op == 19 && i->xo == 16)
+			format(0, i, "(LR)");
+	} else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+addi(Opcode *o, Instr *i)
+{
+	if (i->op==14 && i->ra == 0)
+		format("MOVW", i, "%i,R%d");
+	else if (i->ra == REGSB) {
+		bprint(i, "MOVW\t$");
+		address(i);
+		bprint(i, ",R%d", i->rd);
+	} else if(i->op==14 && i->simm < 0) {
+		bprint(i, "SUB\t$%d,R%d", -i->simm, i->ra);
+		if(i->rd != i->ra)
+			bprint(i, ",R%d", i->rd);
+	} else if(i->ra == i->rd) {
+		format(o->mnemonic, i, "%i");
+		bprint(i, ",R%d", i->rd);
+	} else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+addis(Opcode *o, Instr *i)
+{
+	long v;
+
+	v = i->immediate;
+	if (i->op==15 && i->ra == 0)
+		bprint(i, "MOVW\t$%lux,R%d", v, i->rd);
+	else if (i->op==15 && i->ra == REGSB) {
+		bprint(i, "MOVW\t$");
+		address(i);
+		bprint(i, ",R%d", i->rd);
+	} else if(i->op==15 && v < 0) {
+		bprint(i, "SUB\t$%d,R%d", -v, i->ra);
+		if(i->rd != i->ra)
+			bprint(i, ",R%d", i->rd);
+	} else {
+		format(o->mnemonic, i, 0);
+		bprint(i, "\t$%ld,R%d", v, i->ra);
+		if(i->rd != i->ra)
+			bprint(i, ",R%d", i->rd);
+	}
+}
+
+static void
+andi(Opcode *o, Instr *i)
+{
+	if (i->ra == i->rs)
+		format(o->mnemonic, i, "%I,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+gencc(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, o->ken);
+}
+
+static void
+gen(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, o->ken);
+	if (i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+ldx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "(R%b),R%d");
+	else
+		format(o->mnemonic, i, "(R%b+R%a),R%d");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+stx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "R%d,(R%b)");
+	else
+		format(o->mnemonic, i, "R%d,(R%b+R%a)");
+	if(i->rc && i->xo != 150)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+fldx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "(R%b),F%d");
+	else
+		format(o->mnemonic, i, "(R%b+R%a),F%d");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+fstx(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "F%d,(R%b)");
+	else
+		format(o->mnemonic, i, "F%d,(R%b+R%a)");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+dcb(Opcode *o, Instr *i)
+{
+	if(i->ra == 0)
+		format(o->mnemonic, i, "(R%b)");
+	else
+		format(o->mnemonic, i, "(R%b+R%a)");
+	if(i->rd)
+		bprint(i, " [illegal Rd]");
+	if(i->rc)
+		bprint(i, " [illegal Rc]");
+}
+
+static void
+lw(Opcode *o, Instr *i, char r)
+{
+	bprint(i, "%s\t", o->mnemonic);
+	address(i);
+	bprint(i, ",%c%d", r, i->rd);
+}
+
+static void
+load(Opcode *o, Instr *i)
+{
+	lw(o, i, 'R');
+}
+
+static void
+fload(Opcode *o, Instr *i)
+{
+	lw(o, i, 'F');
+}
+
+static void
+sw(Opcode *o, Instr *i, char r)
+{
+	char *m;
+	Symbol s;
+	Loc l;
+
+	m = o->mnemonic;
+	if (i->rs == REGSP) {
+		l.type = LADDR;
+		l.addr = i->addr;
+		if (findsym(l, CTEXT, &s)>=0) {
+			l.type = LOFFSET;
+			l.reg = "SP";
+			l.offset = i->immediate;
+			if (findlsym(&s, l, &s) >= 0) {
+				bprint(i, "%s\t%c%d,%s-%d(SP)", m, r, i->rd,
+					s.name, i->immediate);
+				return;
+			}
+		}
+	}
+	if (i->rs == REGSB && mach->sb) {
+		bprint(i, "%s\t%c%d,", m, r, i->rd);
+		address(i);
+		return;
+	}
+	if (r == 'F')
+		format(m, i, "F%d,%l");
+	else
+		format(m, i, o->ken);
+}
+
+static void
+store(Opcode *o, Instr *i)
+{
+	sw(o, i, 'R');
+}
+
+static void
+fstore(Opcode *o, Instr *i)
+{
+	sw(o, i, 'F');
+}
+
+static void
+shifti(Opcode *o, Instr *i)
+{
+	if (i->ra == i->rs)
+		format(o->mnemonic, i, "$%k,R%a");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+shift(Opcode *o, Instr *i)
+{
+	if (i->ra == i->rs)
+		format(o->mnemonic, i, "R%b,R%a");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+add(Opcode *o, Instr *i)
+{
+	if (i->rd == i->ra)
+		format(o->mnemonic, i, "R%b,R%d");
+	else if (i->rd == i->rb)
+		format(o->mnemonic, i, "R%a,R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static void
+sub(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, 0);
+	bprint(i, "\t");
+	if(i->op == 31) {
+		bprint(i, "\tR%d,R%d", i->ra, i->rb);	/* subtract Ra from Rb */
+		if(i->rd != i->rb)
+			bprint(i, ",R%d", i->rd);
+	} else
+		bprint(i, "\tR%d,$%d,R%d", i->ra, i->simm, i->rd);
+}
+
+#define div qdiv
+
+static void
+div(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, 0);
+	if(i->op == 31)
+		bprint(i, "\tR%d,R%d", i->rb, i->ra);
+	else
+		bprint(i, "\t$%d,R%d", i->simm, i->ra);
+	if(i->ra != i->rd)
+		bprint(i, ",R%d", i->rd);
+}
+
+static void
+and(Opcode *o, Instr *i)
+{
+	if (i->op == 31) {
+		/* Rb,Rs,Ra */
+		if (i->ra == i->rs)
+			format(o->mnemonic, i, "R%b,R%a");
+		else if (i->ra == i->rb)
+			format(o->mnemonic, i, "R%s,R%a");
+		else
+			format(o->mnemonic, i, o->ken);
+	} else {
+		/* imm,Rs,Ra */
+		if (i->ra == i->rs)
+			format(o->mnemonic, i, "%I,R%a");
+		else
+			format(o->mnemonic, i, o->ken);
+	}
+}
+
+static void
+or(Opcode *o, Instr *i)
+{
+	if (i->op == 31) {
+		/* Rb,Rs,Ra */
+		if (i->rs == 0 && i->ra == 0 && i->rb == 0)
+			format("NOP", i, 0);
+		else if (i->rs == i->rb)
+			format("MOVW", i, "R%b,R%a");
+		else
+			and(o, i);
+	} else
+		and(o, i);
+}
+
+static void
+shifted(Opcode *o, Instr *i)
+{
+	format(o->mnemonic, i, 0);
+	bprint(i, "\t$%lux,", (ulong)i->uimm<<16);
+	if (i->rs == i->ra)
+		bprint(i, "R%d", i->ra);
+	else
+		bprint(i, "R%d,R%d", i->rs, i->ra);
+}
+
+static void
+neg(Opcode *o, Instr *i)
+{
+	if (i->rd == i->ra)
+		format(o->mnemonic, i, "R%d");
+	else
+		format(o->mnemonic, i, o->ken);
+}
+
+static	char	ir2[] = "R%a,R%d";		/* reverse of IBM order */
+static	char	ir3[] = "R%b,R%a,R%d";
+static	char	ir3r[] = "R%a,R%b,R%d";
+static	char	il3[] = "R%b,R%s,R%a";
+static	char	il2u[] = "%I,R%a,R%d";
+static	char	il3s[] = "$%k,R%s,R%a";
+static	char	il2[] = "R%s,R%a";
+static	char	icmp3[] = "R%a,R%b,%D";
+static	char	cr3op[] = "%b,%a,%d";
+static	char	ir2i[] = "%i,R%a,R%d";
+static	char	fp2[] = "F%b,F%d";
+static	char	fp3[] = "F%b,F%a,F%d";
+static	char	fp3c[] = "F%c,F%a,F%d";
+static	char	fp4[] = "F%a,F%c,F%b,F%d";
+static	char	fpcmp[] = "F%a,F%b,%D";
+static	char	ldop[] = "%l,R%d";
+static	char	stop[] = "R%d,%l";
+static	char	fldop[] = "%l,F%d";
+static	char	fstop[] = "F%d,%l";
+static	char	rlim[] = "R%b,R%s,$%z,R%a";
+static	char	rlimi[] = "$%k,R%s,$%z,R%a";
+
+#define	OEM	IBF(~0,22,30)
+#define	FP4	IBF(~0,26,30)
+#define	ALL	(~0)
+/*
+notes:
+	10-26: crfD = rD>>2; rD&3 mbz
+		also, L bit (bit 10) mbz or selects 64-bit operands
+*/
+
+static Opcode opcodes[] = {
+	{31,	360,	OEM,	"ABS%V%C",	0,	ir2},	/* POWER */
+
+	{31,	266,	OEM,	"ADD%V%C",	add,	ir3},
+	{31,	 10,	OEM,	"ADDC%V%C",	add,	ir3},
+	{31,	138,	OEM,	"ADDE%V%C",	add,	ir3},
+	{14,	0,	0,	"ADD",		addi,	ir2i},
+	{12,	0,	0,	"ADDC",		addi,	ir2i},
+	{13,	0,	0,	"ADDCCC",	addi,	ir2i},
+	{15,	0,	0,	"ADD",		addis,	0},
+	{31,	234,	OEM,	"ADDME%V%C",	gencc,	ir2},
+	{31,	202,	OEM,	"ADDZE%V%C",	gencc,	ir2},
+
+	{31,	28,	ALL,	"AND%C",	and,	il3},
+	{31,	60,	ALL,	"ANDN%C",	and,	il3},
+	{28,	0,	0,	"ANDCC",		andi,	il2u},
+	{29,	0,	0,	"ANDCC",		shifted, 0},
+
+	{18,	0,	0,	"B%L",		gencc,	"%j"},
+	{16,	0,	0,	"BC%L",		branch,	"%d,%a,%J"},
+	{19,	528,	ALL,	"BC%L",		branch,	"%d,%a,(CTR)"},
+	{19,	16,	ALL,	"BC%L",		branch,	"%d,%a,(LR)"},
+
+	{31,	531,	ALL,	"CLCS",		gen,	ir2},	/* POWER */
+
+	{31,	0,	ALL,	"CMP",		0,	icmp3},
+	{11,	0,	0,	"CMP",		0,	"R%a,%i,%D"},
+	{31,	32,	ALL,	"CMPU",		0,	icmp3},
+	{10,	0,	0,	"CMPU",		0,	"R%a,%I,%D"},
+
+	{31,	26,	ALL,	"CNTLZ%C",	gencc,	ir2},
+
+	{19,	257,	ALL,	"CRAND",	gen,	cr3op},
+	{19,	129,	ALL,	"CRANDN",	gen,	cr3op},
+	{19,	289,	ALL,	"CREQV",	gen,	cr3op},
+	{19,	225,	ALL,	"CRNAND",	gen,	cr3op},
+	{19,	33,	ALL,	"CRNOR",	gen,	cr3op},
+	{19,	449,	ALL,	"CROR",		gen,	cr3op},
+	{19,	417,	ALL,	"CRORN",	gen,	cr3op},
+	{19,	193,	ALL,	"CRXOR",	gen,	cr3op},
+
+	{31,	86,	ALL,	"DCBF",		dcb,	0},
+	{31,	470,	ALL,	"DCBI",		dcb,	0},
+	{31,	54,	ALL,	"DCBST",	dcb,	0},
+	{31,	278,	ALL,	"DCBT",		dcb,	0},
+	{31,	246,	ALL,	"DCBTST",	dcb,	0},
+	{31,	1014,	ALL,	"DCBZ",		dcb,	0},
+
+	{31,	331,	OEM,	"DIV%V%C",	div,	ir3},	/* POWER */
+	{31,	363,	OEM,	"DIVS%V%C",	div,	ir3},	/* POWER */
+	{31,	491,	OEM,	"DIVW%V%C",	div,	ir3},
+	{31,	459,	OEM,	"DIVWU%V%C",	div,	ir3},
+
+	{31,	264,	OEM,	"DOZ%V%C",	gencc,	ir3r},	/* POWER */
+	{9,	0,	0,	"DOZ",		gen,	ir2i},	/* POWER */
+
+	{31,	310,	ALL,	"ECIWX",	ldx,	0},
+	{31,	438,	ALL,	"ECOWX",	stx,	0},
+	{31,	854,	ALL,	"EIEIO",	gen,	0},
+
+	{31,	284,	ALL,	"EQV%C",	gencc,	il3},
+
+	{31,	954,	ALL,	"EXTSB%C",	gencc,	il2},
+	{31,	922,	ALL,	"EXTSH%C",	gencc,	il2},
+
+	{63,	264,	ALL,	"FABS%C",	gencc,	fp2},
+	{63,	21,	ALL,	"FADD%C",	gencc,	fp3},
+	{59,	21,	ALL,	"FADDS%C",	gencc,	fp3},
+	{63,	32,	ALL,	"FCMPO",	gen,	fpcmp},
+	{63,	0,	ALL,	"FCMPU",	gen,	fpcmp},
+	{63,	14,	ALL,	"FCTIW%C",	gencc,	fp2},
+	{63,	15,	ALL,	"FCTIWZ%C",	gencc,	fp2},
+	{63,	18,	ALL,	"FDIV%C",	gencc,	fp3},
+	{59,	18,	ALL,	"FDIVS%C",	gencc,	fp3},
+	{63,	29,	FP4,	"FMADD%C",	gencc,	fp4},
+	{59,	29,	FP4,	"FMADDS%C",	gencc,	fp4},
+	{63,	72,	ALL,	"FMOVD%C",	gencc,	fp2},
+	{63,	28,	FP4,	"FMSUB%C",	gencc,	fp4},
+	{59,	28,	FP4,	"FMSUBS%C",	gencc,	fp4},
+	{63,	25,	FP4,	"FMUL%C",	gencc,	fp3c},
+	{59,	25,	FP4,	"FMULS%C",	gencc,	fp3c},
+	{63,	136,	ALL,	"FNABS%C",	gencc,	fp2},
+	{63,	40,	ALL,	"FNEG%C",	gencc,	fp2},
+	{63,	31,	FP4,	"FNMADD%C",	gencc,	fp4},
+	{59,	31,	FP4,	"FNMADDS%C",	gencc,	fp4},
+	{63,	30,	FP4,	"FNMSUB%C",	gencc,	fp4},
+	{59,	30,	FP4,	"FNMSUBS%C",	gencc,	fp4},
+	{63,	12,	ALL,	"FRSP%C",	gencc,	fp2},
+	{63,	20,	FP4,	"FSUB%C",	gencc,	fp3},
+	{59,	20,	FP4,	"FSUBS%C",	gencc,	fp3},
+
+	{31,	982,	ALL,	"ICBI",		dcb,	0},
+	{19,	150,	ALL,	"ISYNC",	gen,	0},
+
+	{34,	0,	0,	"MOVBZ",	load,	ldop},
+	{35,	0,	0,	"MOVBZU",	load,	ldop},
+	{31,	119,	ALL,	"MOVBZU",	ldx,	0},
+	{31,	87,	ALL,	"MOVBZ",	ldx,	0},
+	{50,	0,	0,	"FMOVD",	fload,	fldop},
+	{51,	0,	0,	"FMOVDU",	fload,	fldop},
+	{31,	631,	ALL,	"FMOVDU",	fldx,	0},
+	{31,	599,	ALL,	"FMOVD",	fldx,	0},
+	{48,	0,	0,	"FMOVS",	load,	fldop},
+	{49,	0,	0,	"FMOVSU",	load,	fldop},
+	{31,	567,	ALL,	"FMOVSU",	fldx,	0},
+	{31,	535,	ALL,	"FMOVS",	fldx,	0},
+	{42,	0,	0,	"MOVH",		load,	ldop},
+	{43,	0,	0,	"MOVHU",	load,	ldop},
+	{31,	375,	ALL,	"MOVHU",	ldx,	0},
+	{31,	343,	ALL,	"MOVH",		ldx,	0},
+	{31,	790,	ALL,	"MOVHBR",	ldx,	0},
+	{40,	0,	0,	"MOVHZ",	load,	ldop},
+	{41,	0,	0,	"MOVHZU",	load,	ldop},
+	{31,	311,	ALL,	"MOVHZU",	ldx,	0},
+	{31,	279,	ALL,	"MOVHZ",	ldx,	0},
+	{46,	0,	0,	"MOVMW",		load,	ldop},
+	{31,	277,	ALL,	"LSCBX%C",	ldx,	0},	/* POWER */
+	{31,	597,	ALL,	"LSW",		gen,	"(R%a),$%n,R%d"},
+	{31,	533,	ALL,	"LSW",		ldx,	0},
+	{31,	20,	ALL,	"LWAR",		ldx,	0},
+	{31,	534,	ALL,	"MOVWBR",		ldx,	0},
+	{32,	0,	0,	"MOVW",		load,	ldop},
+	{33,	0,	0,	"MOVWU",	load,	ldop},
+	{31,	55,	ALL,	"MOVWU",	ldx,	0},
+	{31,	23,	ALL,	"MOVW",		ldx,	0},
+
+	{31,	29,	ALL,	"MASKG%C",	gencc,	"R%s:R%b,R%d"},	/* POWER */
+	{31,	541,	ALL,	"MASKIR%C",	gencc,	"R%s,R%b,R%a"},	/* POWER */
+
+	{19,	0,	ALL,	"MOVFL",	gen,	"%S,%D"},
+	{63,	64,	ALL,	"MOVCRFS",	gen,	"%S,%D"},
+	{31,	512,	ALL,	"MOVW",	gen,	"XER,%D"},
+	{31,	19,	ALL,	"MOVW",	gen,	"CR,R%d"},
+
+	{63,	583,	ALL,	"MOVW%C",	gen,	"FPSCR, F%d"},	/* mffs */
+	{31,	83,	ALL,	"MOVW",		gen,	"MSR,R%d"},
+	{31,	339,	ALL,	"MOVW",		gen,	"%P,R%d"},
+	{31,	595,	ALL,	"MOVW",		gen,	"SEG(%a),R%d"},
+	{31,	659,	ALL,	"MOVW",		gen,	"SEG(R%b),R%d"},
+	{31,	144,	ALL,	"MOVFL",	gen,	"R%s,%m,CR"},
+	{63,	70,	ALL,	"MTFSB0%C",	gencc,	"%D"},
+	{63,	38,	ALL,	"MTFSB1%C",	gencc,	"%D"},
+	{63,	711,	ALL,	"MOVFL%C",	gencc,	"F%b,%M,FPSCR"},	/* mtfsf */
+	{63,	134,	ALL,	"MOVFL%C",	gencc,	"%K,%D"},
+	{31,	146,	ALL,	"MOVW",		gen,	"R%s,MSR"},
+	{31,	467,	ALL,	"MOVW",		gen,	"R%s,%P"},
+	{31,	210,	ALL,	"MOVW",		gen,	"R%s,SEG(%a)"},
+	{31,	242,	ALL,	"MOVW",		gen,	"R%s,SEG(R%b)"},
+
+	{31,	107,	OEM,	"MUL%V%C",	gencc,	ir3},	/* POWER */
+	{31,	75,	ALL,	"MULHW%C",	gencc,	ir3},	/* POWER */
+	{31,	11,	ALL,	"MULHWU%C",	gencc,	ir3},	/* POWER */
+
+	{31,	235,	OEM,	"MULLW%V%C",	gencc,	ir3},
+	{7,	0,	0,	"MULLW",	div,	"%i,R%a,R%d"},
+
+	{31,	488,	OEM,	"NABS%V%C",	neg,	ir2},	/* POWER */
+
+	{31,	476,	ALL,	"NAND%C",	gencc,	il3},
+	{31,	104,	OEM,	"NEG%V%C",	neg,	ir2},
+	{31,	124,	ALL,	"NOR%C",	gencc,	il3},
+	{31,	444,	ALL,	"OR%C",	or,	il3},
+	{31,	412,	ALL,	"ORN%C",	or,	il3},
+	{24,	0,	0,	"OR",		and,	"%I,R%d,R%a"},
+	{25,	0,	0,	"OR",		shifted, 0},
+
+	{19,	50,	ALL,	"RFI",		gen,	0},
+
+	{22,	0,	0,	"RLMI%C",	gencc,	rlim},	/* POWER */
+	{20,	0,	0,	"RLWMI%C",	gencc,	rlimi},
+	{21,	0,	0,	"RLWNM%C",	gencc,	rlimi},
+	{23,	0,	0,	"RLWNM%C",	gencc,	rlim},
+
+	{31,	537,	ALL,	"RRIB%C",	gencc,	il3},	/* POWER */
+
+	{17,	1,	ALL,	"SYSCALL",	gen,	0},
+
+	{31,	153,	ALL,	"SLE%C",	shift,	il3},	/* POWER */
+	{31,	217,	ALL,	"SLEQ%C",	shift,	il3},	/* POWER */
+	{31,	184,	ALL,	"SLQ%C",	shifti,	il3s},	/* POWER */
+	{31,	248,	ALL,	"SLLQ%C",	shifti,	il3s},	/* POWER */
+	{31,	216,	ALL,	"SLLQ%C",	shift,	il3},	/* POWER */
+	{31,	152,	ALL,	"SLQ%C",	shift,	il3},	/* POWER */
+
+	{31,	24,	ALL,	"SLW%C",	shift,	il3},
+
+	{31,	920,	ALL,	"SRAQ%C",	shift,	il3},	/* POWER */
+	{31,	952,	ALL,	"SRAQ%C",	shifti,	il3s},	/* POWER */
+
+	{31,	792,	ALL,	"SRAW%C",	shift,	il3},
+	{31,	824,	ALL,	"SRAW%C",	shifti,	il3s},
+
+	{31,	665,	ALL,	"SRE%C",	shift,	il3},	/* POWER */
+	{31,	921,	ALL,	"SREA%C",	shift,	il3},	/* POWER */
+	{31,	729,	ALL,	"SREQ%C",	shift,	il3},	/* POWER */
+	{31,	696,	ALL,	"SRQ%C",	shifti,	il3s},	/* POWER */
+	{31,	760,	ALL,	"SRLQ%C",	shifti,	il3s},	/* POWER */
+	{31,	728,	ALL,	"SRLQ%C",	shift,	il3},	/* POWER */
+	{31,	664,	ALL,	"SRQ%C",	shift,	il3},	/* POWER */
+
+	{31,	536,	ALL,	"SRW%C",	shift,	il3},
+
+	{38,	0,	0,	"MOVB",		store,	stop},
+	{39,	0,	0,	"MOVBU",	store,	stop},
+	{31,	247,	ALL,	"MOVBU",	stx,	0},
+	{31,	215,	ALL,	"MOVB",		stx,	0},
+	{54,	0,	0,	"FMOVD",	fstore,	fstop},
+	{55,	0,	0,	"FMOVDU",	fstore,	fstop},
+	{31,	759,	ALL,	"FMOVDU",	fstx,	0},
+	{31,	727,	ALL,	"FMOVD",	fstx,	0},
+	{52,	0,	0,	"FMOVS",	fstore,	fstop},
+	{53,	0,	0,	"FMOVSU",	fstore,	fstop},
+	{31,	695,	ALL,	"FMOVSU",	fstx,	0},
+	{31,	663,	ALL,	"FMOVS",	fstx,	0},
+	{44,	0,	0,	"MOVH",		store,	stop},
+	{31,	918,	ALL,	"MOVHBR",	stx,	0},
+	{45,	0,	0,	"MOVHU",	store,	stop},
+	{31,	439,	ALL,	"MOVHU",	stx,	0},
+	{31,	407,	ALL,	"MOVH",		stx,	0},
+	{47,	0,	0,	"MOVMW",		store,	stop},
+	{31,	725,	ALL,	"STSW",		gen,	"R%d,$%n,(R%a)"},
+	{31,	661,	ALL,	"STSW",		stx,	0},
+	{36,	0,	0,	"MOVW",		store,	stop},
+	{31,	662,	ALL,	"MOVWBR",	stx,	0},
+	{31,	150,	ALL,	"STWCCC",		stx,	0},
+	{37,	0,	0,	"MOVWU",	store,	stop},
+	{31,	183,	ALL,	"MOVWU",	stx,	0},
+	{31,	151,	ALL,	"MOVW",		stx,	0},
+
+	{31,	40,	OEM,	"SUB%V%C",	sub,	ir3},
+	{31,	8,	OEM,	"SUBC%V%C",	sub,	ir3},
+	{31,	136,	OEM,	"SUBE%V%C",	sub,	ir3},
+	{8,	0,	0,	"SUBC",		gen,	"R%a,%i,R%d"},
+	{31,	232,	OEM,	"SUBME%V%C",	sub,	ir2},
+	{31,	200,	OEM,	"SUBZE%V%C",	sub,	ir2},
+
+	{31,	598,	ALL,	"SYNC",		gen,	0},
+	{31,	370,	ALL,	"TLBIA",	gen,	0},
+	{31,	306,	ALL,	"TLBIE",	gen,	"R%b"},
+	{31,	1010,	ALL,	"TLBLI",	gen,	"R%b"},
+	{31,	978,	ALL,	"TLBLD",	gen,	"R%b"},
+	{31,	4,	ALL,	"TW",		gen,	"%d,R%a,R%b"},
+	{3,	0,	0,	"TW",		gen,	"%d,R%a,%i"},
+
+	{31,	316,	ALL,	"XOR",		and,	il3},
+	{26,	0,	0,	"XOR",		and,	il2u},
+	{27,	0,	0,	"XOR",		shifted, 0},
+
+	{0},
+};
+
+typedef struct Spr Spr;
+struct Spr {
+	int	n;
+	char	*name;
+};
+
+static	Spr	sprname[] = {
+	{0, "MQ"},
+	{1, "XER"},
+	{268, "TBL"},
+	{269, "TBU"},
+	{8, "LR"},
+	{9, "CTR"},
+	{528, "IBAT0U"},
+	{529, "IBAT0L"},
+	{530, "IBAT1U"},
+	{531, "IBAT1L"},
+	{532, "IBAT2U"},
+	{533, "IBAT2L"},
+	{534, "IBAT3U"},
+	{535, "IBAT3L"},
+	{536, "DBAT0U"},
+	{537, "DBAT0L"},
+	{538, "DBAT1U"},
+	{539, "DBAT1L"},
+	{540, "DBAT2U"},
+	{541, "DBAT2L"},
+	{542, "DBAT3U"},
+	{543, "DBAT3L"},
+	{25, "SDR1"},
+	{19, "DAR"},
+	{272, "SPRG0"},
+	{273, "SPRG1"},
+	{274, "SPRG2"},
+	{275, "SPRG3"},
+	{18, "DSISR"},
+	{26, "SRR0"},
+	{27, "SRR1"},
+	{284, "TBLW"},
+	{285, "TBUW"},	
+	{22, "DEC"},
+	{282, "EAR"},
+	{1008, "HID0"},
+	{1009, "HID1"},
+	{976, "DMISS"},
+	{977, "DCMP"},
+	{978, "HASH1"},
+	{979, "HASH2"},
+	{980, "IMISS"},
+	{981, "ICMP"},
+	{982, "RPA"},
+	{1010, "IABR"},
+	{0,0},
+};
+
+static void
+format(char *mnemonic, Instr *i, char *f)
+{
+	int n, s;
+	ulong mask;
+
+	if (mnemonic)
+		format(0, i, mnemonic);
+	if (f == 0)
+		return;
+	if (mnemonic)
+		bprint(i, "\t");
+	for ( ; *f; f++) {
+		if (*f != '%') {
+			bprint(i, "%c", *f);
+			continue;
+		}
+		switch (*++f) {
+		case 'V':
+			if(i->oe)
+				bprint(i, "V");
+			break;
+
+		case 'C':
+			if(i->rc)
+				bprint(i, "CC");
+			break;
+
+		case 'a':
+			bprint(i, "%d", i->ra);
+			break;
+
+		case 'b':
+			bprint(i, "%d", i->rb);
+			break;
+
+		case 'c':
+			bprint(i, "%d", i->frc);
+			break;
+
+		case 'd':
+		case 's':
+			bprint(i, "%d", i->rd);
+			break;
+
+		case 'S':
+			if(i->ra & 3)
+				bprint(i, "CR(INVAL:%d)", i->ra);
+			else if(i->op == 63)
+				bprint(i, "FPSCR(%d)", i->crfs);
+			else
+				bprint(i, "CR(%d)", i->crfs);
+			break;
+
+		case 'D':
+			if(i->rd & 3)
+				bprint(i, "CR(INVAL:%d)", i->rd);
+			else if(i->op == 63)
+				bprint(i, "FPSCR(%d)", i->crfd);
+			else
+				bprint(i, "CR(%d)", i->crfd);
+			break;
+
+		case 'l':
+			if(i->simm < 0)
+				bprint(i, "-%lx(R%d)", -i->simm, i->ra);
+			else
+				bprint(i, "%lx(R%d)", i->simm, i->ra);
+			break;
+
+		case 'i':
+			bprint(i, "$%ld", i->simm);
+			break;
+
+		case 'I':
+			bprint(i, "$%lx", i->uimm);
+			break;
+
+		case 'w':
+			bprint(i, "[%lux]", i->w0);
+			break;
+
+		case 'P':
+			n = ((i->spr&0x1f)<<5)|((i->spr>>5)&0x1f);
+			for(s=0; sprname[s].name; s++)
+				if(sprname[s].n == n)
+					break;
+			if(sprname[s].name) {
+				if(s < 10)
+					bprint(i, sprname[s].name);
+				else
+					bprint(i, "SPR(%s)", sprname[s].name);
+			} else
+				bprint(i, "SPR(%d)", n);
+			break;
+
+		case 'n':
+			bprint(i, "%d", i->nb==0? 32: i->nb);	/* eg, pg 10-103 */
+			break;
+
+		case 'm':
+			bprint(i, "%lx", i->crm);
+			break;
+
+		case 'M':
+			bprint(i, "%lx", i->fm);
+			break;
+
+		case 'z':
+			if(i->mb <= i->me)
+				mask = ((ulong)~0L>>i->mb) & (~0L<<(31-i->me));
+			else
+				mask = ~(((ulong)~0L>>(i->me+1)) & (~0L<<(31-(i->mb-1))));
+			bprint(i, "%lux", mask);
+			break;
+
+		case 'k':
+			bprint(i, "%d", i->sh);
+			break;
+
+		case 'K':
+			bprint(i, "$%x", i->imm);
+			break;
+
+		case 'L':
+			if(i->lk)
+				bprint(i, "L");
+			break;
+
+		case 'j':
+			if(i->aa)
+				pglobal(i, i->li, 1, "(SB)");
+			else
+				pglobal(i, i->addr+i->li, 1, "");
+			break;
+
+		case 'J':
+			if(i->aa)
+				pglobal(i, i->bd, 1, "(SB)");
+			else
+				pglobal(i, i->addr+i->bd, 1, "");
+			break;
+
+		case '\0':
+			bprint(i, "%%");
+			return;
+
+		default:
+			bprint(i, "%%%c", *f);
+			break;
+		}
+	}
+}
+
+static int
+printins(Map *map, ulong pc, char *buf, int n)
+{
+	Instr i;
+	Opcode *o;
+
+	mymap = map;
+	memset(&i, 0, sizeof(i));
+	i.curr = buf;
+	i.end = buf+n-1;
+	if(mkinstr(pc, &i) < 0)
+		return -1;
+	for(o = opcodes; o->mnemonic != 0; o++)
+		if(i.op == o->op && (i.xo & o->xomask) == o->xo) {
+			if (o->f)
+				(*o->f)(o, &i);
+			else
+				format(o->mnemonic, &i, o->ken);
+			return i.size*4;
+		}
+	bprint(&i, "unknown %lux", i.w0);
+	return i.size*4;
+}
+
+static int
+powerdas(Map *map, ulong pc, char modifier, char *buf, int n)
+{
+	USED(modifier);
+	return printins(map, pc, buf, n);
+}
+
+static int
+powerhexinst(Map *map, ulong pc, char *buf, int n)
+{
+	Instr instr;
+
+	mymap = map;
+	memset(&instr, 0, sizeof(instr));
+	instr.curr = buf;
+	instr.end = buf+n-1;
+	if (mkinstr(pc, &instr) < 0)
+		return -1;
+	if (instr.end-instr.curr > 8)
+		instr.curr = _hexify(instr.curr, instr.w0, 7);
+	if (instr.end-instr.curr > 9 && instr.size == 2) {
+		*instr.curr++ = ' ';
+		instr.curr = _hexify(instr.curr, instr.w1, 7);
+	}
+	*instr.curr = 0;
+	return instr.size*4;
+}
+
+static int
+powerinstlen(Map *map, ulong pc)
+{
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	return i.size*4;
+}
+
+static int
+powerfoll(Map *map, Regs *regs, ulong pc, ulong *foll)
+{
+	char *reg;
+	Instr i;
+
+	mymap = map;
+	if (mkinstr(pc, &i) < 0)
+		return -1;
+	foll[0] = pc+4;
+	foll[1] = pc+4;
+	switch(i.op) {
+	default:
+		return 1;
+
+	case 18:	/* branch */
+		foll[0] = i.li;
+		if(!i.aa)
+			foll[0] += pc;
+		break;
+			
+	case 16:	/* conditional branch */
+		foll[0] = i.bd;
+		if(!i.aa)
+			foll[0] += pc;
+		break;
+
+	case 19:	/* conditional branch to register */
+		if(i.xo == 528)
+			reg = "CTR";
+		else if(i.xo == 16)
+			reg = "LR";
+		else
+			return 1;	/* not a branch */
+		if(rget(regs, reg, &foll[0]) < 0)
+			return -1;
+		break;
+	}
+	if(i.lk)
+		return 2;
+	return 1;
+}
+
+#define	REGOFF(x)	(ulong) (&((struct Ureg *) 0)->x)
+
+#define SP		REGOFF(r1)
+#define PC		REGOFF(pc)
+#define	R3		REGOFF(r3)	/* return reg */
+#define	LR		REGOFF(lr)
+#define R31		REGOFF(r31)
+#define FP_REG(x)	(R31+4+8*(x))
+
+#define	REGSIZE		sizeof(struct Ureg)
+#define	FPREGSIZE	(8*33)	
+
+Regdesc powerreglist[] =
+{
+	{"CAUSE",	REGOFF(cause),	RINT|RRDONLY,	'X'},
+	{"SRR1",	REGOFF(srr1),	RINT|RRDONLY,	'X'},
+	{"PC",		REGOFF(pc),	RINT,		'X'},
+	{"LR",		REGOFF(lr),	RINT,		'X'},
+	{"CR",		REGOFF(cr),	RINT,		'X'},
+	{"XER",		REGOFF(xer),	RINT,		'X'},
+	{"CTR",		REGOFF(ctr),	RINT,		'X'},
+	{"PC",		PC,		RINT,		'X'},
+	{"SP",		SP,		RINT,		'X'},
+	{"R0",		REGOFF(r0),	RINT,		'X'},
+	/* R1 is SP */
+	{"R2",		REGOFF(r2),	RINT,		'X'},
+	{"R3",		REGOFF(r3),	RINT,		'X'},
+	{"R4",		REGOFF(r4),	RINT,		'X'},
+	{"R5",		REGOFF(r5),	RINT,		'X'},
+	{"R6",		REGOFF(r6),	RINT,		'X'},
+	{"R7",		REGOFF(r7),	RINT,		'X'},
+	{"R8",		REGOFF(r8),	RINT,		'X'},
+	{"R9",		REGOFF(r9),	RINT,		'X'},
+	{"R10",		REGOFF(r10),	RINT,		'X'},
+	{"R11",		REGOFF(r11),	RINT,		'X'},
+	{"R12",		REGOFF(r12),	RINT,		'X'},
+	{"R13",		REGOFF(r13),	RINT,		'X'},
+	{"R14",		REGOFF(r14),	RINT,		'X'},
+	{"R15",		REGOFF(r15),	RINT,		'X'},
+	{"R16",		REGOFF(r16),	RINT,		'X'},
+	{"R17",		REGOFF(r17),	RINT,		'X'},
+	{"R18",		REGOFF(r18),	RINT,		'X'},
+	{"R19",		REGOFF(r19),	RINT,		'X'},
+	{"R20",		REGOFF(r20),	RINT,		'X'},
+	{"R21",		REGOFF(r21),	RINT,		'X'},
+	{"R22",		REGOFF(r22),	RINT,		'X'},
+	{"R23",		REGOFF(r23),	RINT,		'X'},
+	{"R24",		REGOFF(r24),	RINT,		'X'},
+	{"R25",		REGOFF(r25),	RINT,		'X'},
+	{"R26",		REGOFF(r26),	RINT,		'X'},
+	{"R27",		REGOFF(r27),	RINT,		'X'},
+	{"R28",		REGOFF(r28),	RINT,		'X'},
+	{"R29",		REGOFF(r29),	RINT,		'X'},
+	{"R30",		REGOFF(r30),	RINT,		'X'},
+	{"R31",		REGOFF(r31),	RINT,		'X'},
+	{"VRSAVE",	REGOFF(vrsave),	RINT,	'X'},
+	{"F0",		FP_REG(0),	RFLT,		'F'},
+	{"F1",		FP_REG(1),	RFLT,		'F'},
+	{"F2",		FP_REG(2),	RFLT,		'F'},
+	{"F3",		FP_REG(3),	RFLT,		'F'},
+	{"F4",		FP_REG(4),	RFLT,		'F'},
+	{"F5",		FP_REG(5),	RFLT,		'F'},
+	{"F6",		FP_REG(6),	RFLT,		'F'},
+	{"F7",		FP_REG(7),	RFLT,		'F'},
+	{"F8",		FP_REG(8),	RFLT,		'F'},
+	{"F9",		FP_REG(9),	RFLT,		'F'},
+	{"F10",		FP_REG(10),	RFLT,		'F'},
+	{"F11",		FP_REG(11),	RFLT,		'F'},
+	{"F12",		FP_REG(12),	RFLT,		'F'},
+	{"F13",		FP_REG(13),	RFLT,		'F'},
+	{"F14",		FP_REG(14),	RFLT,		'F'},
+	{"F15",		FP_REG(15),	RFLT,		'F'},
+	{"F16",		FP_REG(16),	RFLT,		'F'},
+	{"F17",		FP_REG(17),	RFLT,		'F'},
+	{"F18",		FP_REG(18),	RFLT,		'F'},
+	{"F19",		FP_REG(19),	RFLT,		'F'},
+	{"F20",		FP_REG(20),	RFLT,		'F'},
+	{"F21",		FP_REG(21),	RFLT,		'F'},
+	{"F22",		FP_REG(22),	RFLT,		'F'},
+	{"F23",		FP_REG(23),	RFLT,		'F'},
+	{"F24",		FP_REG(24),	RFLT,		'F'},
+	{"F25",		FP_REG(25),	RFLT,		'F'},
+	{"F26",		FP_REG(26),	RFLT,		'F'},
+	{"F27",		FP_REG(27),	RFLT,		'F'},
+	{"F28",		FP_REG(28),	RFLT,		'F'},
+	{"F29",		FP_REG(29),	RFLT,		'F'},
+	{"F30",		FP_REG(30),	RFLT,		'F'},
+	{"F31",		FP_REG(31),	RFLT,		'F'},
+	{"FPSCR",	FP_REG(32)+4,	RFLT,		'X'},
+	{  0 }
+};
+
+static char *powerwindregs[] = 
+{
+	"PC",
+	"SP",
+	"LR",
+	0,
+};
+
+static int
+powerunwind(Map *map, Regs *regs, ulong *next)
+{
+	/*
+	 * This is tremendously hard.  The best we're going to
+	 * do without better debugger support is trace through
+	 * the stack frame links and pull the link registers out of 8(R1).
+	 * Anything more requires knowing which registers got saved,
+	 * and the compiler appears not to record that.  Gdb appears
+	 * to disassemble the function prologues in order to figure
+	 * this out.
+	 */
+	ulong b[2];
+
+	// evaluate lr
+	// if in this function, no good - go to saved one.
+	// set next[sp] to *cur[sp]
+	// set next[pc] to lr
+	// set next[lr] to lr
+	// 
+	werrstr("powerunwind not implemented");
+	return -1;
+}
+
+	/* the machine description */
+Mach machpower =
+{
+	"power",
+	MPOWER,		/* machine type */
+	powerreglist,	/* register set */
+	REGSIZE,	/* number of bytes in register set */
+	FPREGSIZE,	/* number of bytes in FP register set */
+	"PC",		/* name of PC */
+	"SP",		/* name of SP */
+	0,		/* name of FP */
+	"LR",		/* name of link register */
+	"setSB",	/* static base register name */
+	0,		/* value */
+	0x1000,		/* page size */
+	0x80000000,	/* kernel base */
+	0,		/* kernel text mask */
+	4,		/* quantization of pc */
+	4,		/* szaddr */
+	4,		/* szreg */
+	4,		/* szfloat */
+	8,		/* szdouble */
+
+	powerwindregs,	/* locations unwound in stack trace */
+	3,
+
+	{0x02, 0x8F, 0xFF, 0xFF},	/* break point */ /* BUG */
+	4,
+
+	powerfoll,		/* following addresses */
+	powerexcep,		/* print exception */
+	powerunwind,		/* stack unwind */
+
+	beswap2,			/* convert short to local byte order */
+	beswap4,			/* convert long to local byte order */
+	beswap8,			/* convert vlong to local byte order */
+	beieeeftoa32,		/* single precision float pointer */
+	beieeeftoa64,		/* double precision float pointer */
+	beieeeftoa80,		/* long double precision floating point */
+
+	powerdas,		/* dissembler */
+	powerdas,		/* plan9-format disassembler */
+	0,			/* commercial disassembler */
+	powerhexinst,		/* print instruction */
+	powerinstlen,		/* instruction size calculation */
+};
+
blob - /dev/null
blob + 144b7042ccaa1947172b0536930120a3ef8cc67f (mode 644)
--- /dev/null
+++ src/libmach/map.c
@@ -0,0 +1,306 @@
+/*
+ * File map routines
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+static int fdrw(Map*, Seg*, ulong, void*, uint, int);
+static int zerorw(Map*, Seg*, ulong, void*, uint, int);
+static int mrw(Map*, ulong, void*, uint, int);
+static int datarw(Map*, Seg*, ulong, void*, uint, int);
+
+Map*
+allocmap(void)
+{
+	return mallocz(sizeof(Map), 1);
+}
+
+void
+freemap(Map *map)
+{
+	if(map == nil)
+		return;
+	free(map->seg);
+	free(map);
+}
+
+int
+addseg(Map *map, Seg seg)
+{
+	Seg *ss;
+
+	if(map == 0){
+		werrstr("invalid map");
+		return -1;
+	}
+
+	ss = realloc(map->seg, (map->nseg+1)*sizeof(ss[0]));
+	if(ss == nil)
+		return -1;
+	map->seg = ss;
+	if(seg.rw == nil){
+		if(seg.name && strcmp(seg.name, "zero") == 0)
+			seg.rw = zerorw;
+		else if(seg.p)
+			seg.rw = datarw;
+		else
+			seg.rw = fdrw;
+	}
+	map->seg[map->nseg] = seg;
+	return map->nseg++;
+}
+
+int
+findseg(Map *map, char *name, char *file)
+{
+	int i;
+
+	if(map == 0)
+		return -1;
+	for(i=0; i<map->nseg; i++){
+		if(name && (!map->seg[i].name || strcmp(map->seg[i].name, name) != 0))
+			continue;
+		if(file && (!map->seg[i].file || strcmp(map->seg[i].file, file) != 0))
+			continue;
+		return i;
+	}
+	werrstr("segment %s in %s not found", name, file);
+	return -1;
+}
+
+int
+addrtoseg(Map *map, ulong addr, Seg *sp)
+{
+	int i;
+	Seg *s;
+
+	if(map == nil){
+		werrstr("no map");
+		return -1;
+	}
+	for(i=map->nseg-1; i>=0; i--){
+		s = &map->seg[i];
+		if(s->base <= addr && addr-s->base < s->size){
+			if(sp)
+				*sp = *s;
+			return i;
+		}
+	}
+	werrstr("address 0x%lux is not mapped", addr);
+	return -1;
+}
+
+int
+addrtosegafter(Map *map, ulong addr, Seg *sp)
+{
+	int i;
+	Seg *s, *best;
+	ulong bdist;
+
+	if(map == nil){
+		werrstr("no map");
+		return -1;
+	}
+
+	/*
+	 * If segments were sorted this would be easier,
+	 * but since segments may overlap, sorting also
+	 * requires splitting and rejoining, and that's just
+	 * too complicated.
+	 */
+	best = nil;
+	bdist = 0;
+	for(i=map->nseg-1; i>=0; i--){
+		s = &map->seg[i];
+		if(s->base > addr){
+			if(best==nil || s->base-addr < bdist){
+				bdist = s->base - addr;
+				best = s;
+			}
+		}
+	}
+	if(best){
+		if(sp)
+			*sp = *best;
+		return best-map->seg;
+	}
+	werrstr("nothing mapped after address 0x%lux", addr);
+	return -1;
+}
+
+void
+removeseg(Map *map, int i)
+{
+	if(map == nil)
+		return;
+	if(i < 0 || i >= map->nseg)
+		return;
+	memmove(&map->seg[i], &map->seg[i+1], (map->nseg-(i+1))*sizeof(Seg));
+	map->nseg--;
+}
+
+int
+get1(Map *map, ulong addr, uchar *a, uint n)
+{
+	return mrw(map, addr, a, n, 1);
+}
+
+int
+get2(Map *map, ulong addr, u16int *u)
+{
+	u16int v;
+
+	if(mrw(map, addr, &v, 2, 1) < 0)
+		return -1;
+	*u = mach->swap2(v);
+	return 2;
+}
+
+int
+get4(Map *map, ulong addr, u32int *u)
+{
+	u32int v;
+
+	if(mrw(map, addr, &v, 4, 1) < 0)
+		return -1;
+	*u = mach->swap4(v);
+	return 4;
+}
+
+int
+get8(Map *map, ulong addr, u64int *u)
+{
+	u64int v;
+
+	if(mrw(map, addr, &v, 4, 1) < 0)
+		return -1;
+	*u = mach->swap8(v);
+	return 8;
+}
+
+int
+put1(Map *map, ulong addr, uchar *a, uint n)
+{
+	return mrw(map, addr, a, n, 0);
+}
+
+int
+put2(Map *map, ulong addr, u16int u)
+{
+	u = mach->swap2(u);
+	return mrw(map, addr, &u, 2, 0);
+}
+
+int
+put4(Map *map, ulong addr, u32int u)
+{
+	u = mach->swap4(u);
+	return mrw(map, addr, &u, 4, 0);
+}
+
+int
+put8(Map *map, ulong addr, u64int u)
+{
+	u = mach->swap8(u);
+	return mrw(map, addr, &u, 8, 0);
+}
+
+static Seg*
+reloc(Map *map, ulong addr, uint n, ulong *off, uint *nn)
+{
+	int i;
+	ulong o;
+
+	if(map == nil){
+		werrstr("invalid map");
+		return nil;
+	}
+
+	for(i=map->nseg-1; i>=0; i--){
+		if(map->seg[i].base <= addr){
+			o = addr - map->seg[i].base;
+			if(o >= map->seg[i].size)
+				continue;
+			if(o+n > map->seg[i].size)
+				*nn = map->seg[i].size - o;
+			else
+				*nn = n;
+			*off = o;
+			return &map->seg[i];
+		}
+	}
+	werrstr("address 0x%lux not mapped", addr);
+	return nil;
+}
+
+static int
+mrw(Map *map, ulong addr, void *a, uint n, int r)
+{
+	uint nn;
+	uint tot;
+	Seg *s;
+	ulong off;
+
+	for(tot=0; tot<n; tot+=nn){
+		s = reloc(map, addr+tot, n-tot, &off, &nn);
+		if(s == nil)
+			return -1;
+		if(s->rw(map, s, off, a, nn, r) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+static int
+fdrw(Map *map, Seg *seg, ulong addr, void *a, uint n, int r)
+{
+	int nn;
+	uint tot;
+	ulong off;
+
+	USED(map);
+	off = seg->offset + addr;
+	for(tot=0; tot<n; tot+=nn){
+		if(r)
+			nn = pread(seg->fd, a, n-tot, off+tot);
+		else
+			nn = pwrite(seg->fd, a, n-tot, off+tot);
+		if(nn < 0)
+			return -1;	
+		if(nn == 0){
+			werrstr("partial %s at address 0x%lux in %s",
+				r ? "read" : "write", off+tot, seg->file);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int
+zerorw(Map *map, Seg *seg, ulong addr, void *a, uint n, int r)
+{
+	USED(map);
+	USED(seg);
+	USED(addr);
+
+	if(r==0){
+		werrstr("cannot write zero segment");
+		return -1;
+	}
+	memset(a, 0, n);
+	return 0;
+}
+
+static int
+datarw(Map *map, Seg *seg, ulong addr, void *a, uint n, int r)
+{
+	USED(map);
+
+	if(r)
+		memmove(a, seg->p+addr, n);
+	else
+		memmove(seg->p+addr, a, n);
+	return 0;
+}
blob - /dev/null
blob + a690a0005e4f6e7face674a51956bff423ca2da5 (mode 644)
--- /dev/null
+++ src/libmach/mkfile
@@ -0,0 +1,58 @@
+<$PLAN9/src/mkhdr
+
+LIB=libmach.a
+
+OFILES=\
+	$SYSNAME.$O\
+	crack.$O\
+	crackelf.$O\
+	crackmacho.$O\
+	dwarf386.$O\
+	dwarfabbrev.$O\
+	dwarfaranges.$O\
+	dwarfcfa.$O\
+	dwarfget.$O\
+	dwarfinfo.$O\
+	dwarfopen.$O\
+	dwarfpc.$O\
+	dwarfpubnames.$O\
+	elf.$O\
+	elfcorefreebsd386.$O\
+	elfcorelinux386.$O\
+	frame.$O\
+	fpformat.$O\
+	hexify.$O\
+	ieee.$O\
+	loc.$O\
+	localaddr.$O\
+	mach.$O\
+	mach386.$O\
+	macho.$O\
+	machocorepower.$O\
+	machpower.$O\
+	map.$O\
+	regs.$O\
+	stabs.$O\
+	swap.$O\
+	sym.$O\
+	symdwarf.$O\
+	symelf.$O\
+	symmacho.$O\
+	symstabs.$O\
+
+HFILES=mach.h
+
+<$PLAN9/src/mksyslib
+CFLAGS=$CFLAGS -I.
+
+elfdump: elfdump.o $LIBDIR/$LIB
+	$LD -o $target $prereq -l9
+
+machodump: machodump.o $LIBDIR/$LIB
+	$LD -o $target $prereq -l9
+
+dwarfdump: dwarfdump.o $LIBDIR/$LIB
+	$LD -o $target $prereq -l9
+
+nm: nm.o $LIBDIR/$LIB
+	$LD -o $target $prereq -l9
blob - /dev/null
blob + b5e369cb39584e5577a950a77f62b7015c462821 (mode 644)
--- /dev/null
+++ src/libmach/nm.c
@@ -0,0 +1,289 @@
+/*
+ * nm.c -- drive nm
+ */
+#include <u.h>
+#include <libc.h>
+#include <ar.h>
+#include <bio.h>
+#include <mach.h>
+
+enum{
+	CHUNK	=	256	/* must be power of 2 */
+};
+
+char	*errs;			/* exit status */
+char	*filename;		/* current file */
+char	symname[]="__.SYMDEF";	/* table of contents file name */
+int	multifile;		/* processing multiple files */
+int	aflag;
+int	gflag;
+int	hflag;
+int	nflag;
+int	sflag;
+int	uflag;
+
+Symbol	**fnames;		/* file path translation table */
+Symbol	**symptr;
+int	nsym;
+Biobuf	bout;
+
+int	cmp(void*, void*);
+void	error(char*, ...);
+void	execsyms(int);
+void	psym(Symbol*, void*);
+void	printsyms(Symbol**, long);
+void	doar(Biobuf*);
+void	dofile(Biobuf*);
+void	zenter(Symbol*);
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+	Biobuf	*bin;
+
+	Binit(&bout, 1, OWRITE);
+	argv0 = argv[0];
+	ARGBEGIN {
+	case 'a':	aflag = 1; break;
+	case 'g':	gflag = 1; break;
+	case 'h':	hflag = 1; break;
+	case 'n':	nflag = 1; break;
+	case 's':	sflag = 1; break;
+	case 'u':	uflag = 1; break;
+	} ARGEND
+	if (argc > 1)
+		multifile++;
+	for(i=0; i<argc; i++){
+		filename = argv[i];
+		bin = Bopen(filename, OREAD);
+		if(bin == 0){
+			error("cannot open %s", filename);
+			continue;
+		}
+		if (isar(bin))
+			doar(bin);
+		else{
+			Bseek(bin, 0, 0);
+			dofile(bin);
+		}
+		Bterm(bin);
+	}
+	exits(errs);
+}
+
+/*
+ * read an archive file,
+ * processing the symbols for each intermediate file in it.
+ */
+void
+doar(Biobuf *bp)
+{
+	int offset, size, obj;
+	char membername[SARNAME];
+
+	multifile = 1;
+	for (offset = Boffset(bp);;offset += size) {
+		size = nextar(bp, offset, membername);
+		if (size < 0) {
+			error("phase error on ar header %ld", offset);
+			return;
+		}
+		if (size == 0)
+			return;
+		if (strcmp(membername, symname) == 0)
+			continue;
+		obj = objtype(bp, 0);
+		if (obj < 0) {
+			error("inconsistent file %s in %s",
+					membername, filename);
+			return;
+		}
+		if (!readar(bp, obj, offset+size, 1)) {
+			error("invalid symbol reference in file %s",
+					membername);
+			return;
+		}
+		filename = membername;
+		nsym=0;
+		objtraverse(psym, 0);
+		printsyms(symptr, nsym);
+	}
+}
+
+/*
+ * process symbols in a file
+ */
+void
+dofile(Biobuf *bp)
+{
+	int obj;
+
+	obj = objtype(bp, 0);
+	if (obj < 0)
+		execsyms(Bfildes(bp));
+	else
+	if (readobj(bp, obj)) {
+		nsym = 0;
+		objtraverse(psym, 0);
+		printsyms(symptr, nsym);
+	}
+}
+
+/*
+ * comparison routine for sorting the symbol table
+ *	this screws up on 'z' records when aflag == 1
+ */
+int
+cmp(void *vs, void *vt)
+{
+	Symbol **s, **t;
+
+	s = vs;
+	t = vt;
+	if(nflag)
+		if((*s)->value < (*t)->value)
+			return -1;
+		else
+			return (*s)->value > (*t)->value;
+	return strcmp((*s)->name, (*t)->name);
+}
+/*
+ * enter a symbol in the table of filename elements
+ */
+void
+zenter(Symbol *s)
+{
+	static int maxf = 0;
+
+	if (s->value > maxf) {
+		maxf = (s->value+CHUNK-1) &~ (CHUNK-1);
+		fnames = realloc(fnames, (maxf+1)*sizeof(*fnames));
+		if(fnames == 0) {
+			error("out of memory", argv0);
+			exits("memory");
+		}
+	}
+	fnames[s->value] = s;
+}
+
+/*
+ * get the symbol table from an executable file, if it has one
+ */
+void
+execsyms(int fd)
+{
+	Fhdr f;
+	Symbol *s;
+	long n;
+
+	seek(fd, 0, 0);
+	if (crackhdr(fd, &f) == 0) {
+		error("Can't read header for %s", filename);
+		return;
+	}
+	if (syminit(fd, &f) < 0)
+		return;
+	s = symbase(&n);
+	nsym = 0;
+	while(n--)
+		psym(s++, 0);
+
+	printsyms(symptr, nsym);
+}
+
+void
+psym(Symbol *s, void* p)
+{
+	USED(p);
+	switch(s->type) {
+	case 'T':
+	case 'L':
+	case 'D':
+	case 'B':
+		if (uflag)
+			return;
+		if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
+			return;
+		break;
+	case 'b':
+	case 'd':
+	case 'l':
+	case 't':
+		if (uflag || gflag)
+			return;
+		if (!aflag && ((s->name[0] == '.' || s->name[0] == '$')))
+			return;
+		break;
+	case 'U':
+		if (gflag)
+			return;
+		break;
+	case 'Z':
+		if (!aflag)
+			return;
+		break;
+	case 'm':
+	case 'f':	/* we only see a 'z' when the following is true*/
+		if(!aflag || uflag || gflag)
+			return;
+		if (strcmp(s->name, ".frame"))
+			zenter(s);
+		break;
+	case 'a':
+	case 'p':
+	case 'z':
+	default:
+		if(!aflag || uflag || gflag)
+			return;
+		break;
+	}
+	symptr = realloc(symptr, (nsym+1)*sizeof(Sym*));
+	if (symptr == 0) {
+		error("out of memory");
+		exits("memory");
+	}
+	symptr[nsym++] = s;
+}
+
+void
+printsyms(Symbol **symptr, long nsym)
+{
+	Symbol *s;
+	char *cp;
+	char path[512];
+
+	if(!sflag)
+		qsort(symptr, nsym, sizeof(*symptr), cmp);
+	while (nsym-- > 0) {
+		s = *symptr++;
+		if (multifile && !hflag)
+			Bprint(&bout, "%s:", filename);
+		if (s->type == 'z') {
+			fileelem(fnames, (uchar *) s->name, path, 512);
+			cp = path;
+		} else
+			cp = s->name;
+		if (s->value || s->type == 'a' || s->type == 'p')
+			Bprint(&bout, "%8lux %c %s\n", s->value, s->type, cp);
+		else
+			Bprint(&bout, "         %c %s\n", s->type, cp);
+	}
+}
+
+void
+error(char *fmt, ...)
+{
+	Fmt f;
+	char buf[128];
+	va_list arg;
+
+	fmtfdinit(&f, 2, buf, sizeof buf);
+	fmtprint(&f, "%s: ", argv0);
+	va_start(arg, fmt);
+	fmtvprint(&f, fmt, arg);
+	va_end(arg);
+	fmtprint(&f, "\n");
+	fmtfdflush(&f);
+	errs = "errors";
+}
blob - /dev/null
blob + 43c04786c7ebc176595aa3728c8d391b6d09ca7b (mode 644)
--- /dev/null
+++ src/libmach/regs.c
@@ -0,0 +1,59 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+
+int
+rput(Regs *regs, char *name, ulong u)
+{
+	if(regs == nil){
+		werrstr("registers not mapped");
+		return -1;
+	}
+	return regs->rw(regs, name, &u, 0);
+}
+
+int
+rget(Regs *regs, char *name, ulong *u)
+{
+	if(regs == nil){
+		*u = ~(ulong)0;
+		werrstr("registers not mapped");
+		return -1;
+	}
+	return regs->rw(regs, name, u, 1);
+}
+
+int
+_uregrw(Regs *regs, char *name, ulong *u, int isr)
+{
+	Regdesc *r;
+	uchar *ureg;
+
+	if(!isr){
+		werrstr("cannot write registers");
+		return -1;
+	}
+
+	if((r = regdesc(name)) == nil)
+		return -1;
+	ureg = ((UregRegs*)regs)->ureg + r->offset;
+
+	switch(r->format){
+	default:
+	case 'X':
+		*u = mach->swap4(*(u32int*)ureg);
+		return 0;
+	}
+}
+
+Regdesc*
+regdesc(char *name)
+{
+	Regdesc *r;
+
+	for(r=mach->reglist; r->name; r++)
+		if(strcmp(r->name, name) == 0)
+			return r;
+	return nil;
+}
+
blob - /dev/null
blob + d34bb864362fd93f2d20bc5eaeec2c843fa438c4 (mode 644)
--- /dev/null
+++ src/libmach/stabs.c
@@ -0,0 +1,54 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "stabs.h"
+
+/*
+http://sources.redhat.com/gdb/onlinedocs/stabs.html
+*/
+
+int
+stabsym(Stab *stabs, int i, StabSym *sym)
+{
+	uchar *p;
+	ulong x;
+
+	if(stabs == nil){
+		werrstr("no stabs");
+		return -1;
+	}
+	if(stabs->e2==nil || stabs->e4==nil){
+		werrstr("no data extractors");
+		return -1;
+	}
+
+	if(i >= stabs->stabsize/12){
+		werrstr("stabs index out of range");
+		return -1;
+	}
+
+	p = stabs->stabbase+i*12;
+	x = stabs->e4(p);
+	if(x == 0)
+		sym->name = nil;
+	else if(x < stabs->strsize)
+		sym->name = stabs->strbase+x;
+	else{
+		werrstr("bad stabs string index");
+		return -1;
+	}
+
+	/*
+	 * In theory, if name ends with a backslash,
+	 * it continues into the next entry.  We could
+	 * rewrite these in place and then zero the next
+	 * few entries, but let's wait until we run across
+	 * some system that generates these.
+	 */
+	sym->type = p[4];
+	sym->other = p[5];
+	sym->desc = stabs->e2(p+6);
+	sym->value = stabs->e4(p+8);
+	return 0;
+}
+
blob - /dev/null
blob + ad67cfe648162d58a0b604f11fcb2b3ddfb18c1f (mode 644)
--- /dev/null
+++ src/libmach/stabs.h
@@ -0,0 +1,117 @@
+typedef struct StabSym StabSym;
+typedef struct Stab Stab;	/* defined in mach.h */
+
+struct StabSym
+{
+	char *name;
+	uchar type;
+	uchar other;
+	u16int desc;
+	u32int value;
+};
+
+enum
+{
+	EXT = 0x01,
+
+	N_UNDEF = 0x00,
+	N_ABS = 0x02,
+	N_TEXT = 0x04,
+	N_DATA = 0x06,
+	N_BSS = 0x08,
+	N_INDR = 0x0A,
+	N_FN_SEQ = 0x0C,
+	N_WEAKU = 0x0D,
+	N_WEAKA = 0x0E,
+	N_WEAKT = 0x0F,
+	N_WEAKD = 0x10,
+	N_WEAKB = 0x11,
+	N_COMM = 0x12,
+	N_SETA = 0x14,
+	N_SETT = 0x16,
+
+	N_GSYM = 0x20,
+	N_FNAME = 0x22,
+	N_FUN = 0x24,
+	N_STSYM = 0x26,
+	N_LCSYM = 0x28,
+	N_MAIN = 0x2A,
+	N_ROSYM = 0x2C,
+	N_PC = 0x30,
+	N_NSYMS = 0x32,
+	N_NOMAP = 0x34,
+	N_OBJ = 0x38,
+	N_OPT = 0x3C,
+	N_RSYM = 0x40,
+	N_M2C = 0x42,
+	N_SLINE = 0x44,
+	N_DSLINE = 0x46,
+	N_BSLINE = 0x48,
+	N_BROWS = 0x48,
+	N_DEFD = 0x4A,
+	N_FLINE = 0x4C,
+	N_EHDECL = 0x50,
+	N_MOD2 = 0x50,
+	N_CATCH = 0x54,
+	N_SSYM = 0x60,
+	N_ENDM = 0x62,
+	N_SO = 0x64,
+	N_ALIAS = 0x6C,
+	N_LSYM = 0x80,
+	N_BINCL = 0x82,
+	N_SOL = 0x84,
+	N_PSYM = 0xA0,
+	N_EINCL = 0xA2,
+	N_ENTRY = 0xA4,
+	N_LBRAC = 0xC0,
+	N_EXCL = 0xC2,
+	N_SCOPE = 0xC4,
+	N_RBRAC = 0xE0,
+	N_BCOMM = 0xE2,
+	N_ECOMM = 0xE4,
+	N_ECOML = 0xE8,
+	N_WITH = 0xEA,
+	N_LENG = 0xFE
+};
+
+/*
+ symbol descriptors
+
+[(0-9\-]	variable on stack
+:		C++ nested symbol
+a		parameter by reference
+b		based variable
+c		constant
+C		conformant array bound
+		name of caught exception (N_CATCH)
+d		fp register variable
+D		fp parameter
+f		file scope function
+F		global function
+G		global variable
+i		register parameter?
+I		nested procedure
+J		nested function
+L		label name
+m		module
+p		arg list parameter
+pP
+pF
+P		register param (N_PSYM)
+		proto of ref fun (N_FUN)
+Q		static procedure
+R		register param
+r		register variable
+S		file scope variable
+s		local variable
+t		type name
+T		sue tag
+v		param by reference
+V		procedure scope static variable
+x		conformant array
+X		function return variable
+
+*/
+
+int stabsym(Stab*, int, StabSym*);
+
blob - /dev/null
blob + b6558234cdbb806b7273795979d2a404d0254c25 (mode 644)
--- /dev/null
+++ src/libmach/swap.c
@@ -0,0 +1,118 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+/*
+ * big-endian short
+ */
+u16int
+beswap2(u16int s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[0]<<8) | p[1];
+}
+
+/*
+ * big-endian long
+ */
+u32int
+beswap4(u32int l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+
+/*
+ * big-endian vlong
+ */
+u64int
+beswap8(u64int v)
+{
+	uchar *p;
+
+	p = (uchar*)&v;
+	return ((u64int)p[0]<<56) | ((u64int)p[1]<<48) | ((u64int)p[2]<<40)
+				 | ((u64int)p[3]<<32) | ((u64int)p[4]<<24)
+				 | ((u64int)p[5]<<16) | ((u64int)p[6]<<8)
+				 | (u64int)p[7];
+}
+
+/*
+ * little-endian short
+ */
+u16int
+leswap2(u16int s)
+{
+	uchar *p;
+
+	p = (uchar*)&s;
+	return (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian long
+ */
+u32int
+leswap4(u32int l)
+{
+	uchar *p;
+
+	p = (uchar*)&l;
+	return (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0];
+}
+
+/*
+ * little-endian vlong
+ */
+u64int
+leswap8(u64int v)
+{
+	uchar *p;
+
+	p = (uchar*)&v;
+	return ((u64int)p[7]<<56) | ((u64int)p[6]<<48) | ((u64int)p[5]<<40)
+				 | ((u64int)p[4]<<32) | ((u64int)p[3]<<24)
+				 | ((u64int)p[2]<<16) | ((u64int)p[1]<<8)
+				 | (u64int)p[0];
+}
+
+u16int
+leload2(uchar *b)
+{
+	return b[0] | (b[1]<<8);
+}
+
+u32int
+leload4(uchar *b)
+{
+	return b[0] | (b[1]<<8) | (b[2]<<16) | (b[3]<<24);
+}
+
+u64int
+leload8(uchar *b)
+{
+	return leload4(b) | ((uvlong)leload4(b+4) << 32);
+}
+
+u16int
+beload2(uchar *b)
+{
+	return (b[0]<<8) | b[1];
+}
+
+u32int
+beload4(uchar *b)
+{
+	return (b[0]<<24) | (b[1]<<16) | (b[2]<<8) | b[3];
+}
+
+u64int
+beload8(uchar *b)
+{
+	return ((uvlong)beload4(b) << 32) | beload4(b+4);
+}
blob - /dev/null
blob + 7953019b41936345a4123d0af6bfa6b58f638db8 (mode 644)
--- /dev/null
+++ src/libmach/sym.c
@@ -0,0 +1,478 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+int machdebug = 0;
+
+Fhdr *fhdrlist;
+static Fhdr *last;
+
+static void
+relocsym(Symbol *dst, Symbol *src, ulong base)
+{
+	if(dst != src)
+		*dst = *src;
+	if(dst->loc.type == LADDR)
+		dst->loc.addr += base;
+	if(dst->hiloc.type == LADDR)
+		dst->hiloc.addr += base;
+}
+
+void
+_addhdr(Fhdr *h)
+{
+	h->next = nil;
+	if(fhdrlist == nil){
+		fhdrlist = h;
+		last = h;
+	}else{
+		last->next = h;
+		last = h;
+	}
+}
+
+void
+_delhdr(Fhdr *h)
+{
+	Fhdr *p;
+
+	if(h == fhdrlist)
+		fhdrlist = h->next;
+	else{
+		for(p=fhdrlist; p && p->next!=h; p=p->next)
+			;
+		if(p)
+			p->next = h->next;
+		if(p->next == nil)
+			last = p;
+	}
+	h->next = nil;
+}
+
+int
+pc2file(ulong pc, char *file, uint nfile, ulong *line)
+{
+	Fhdr *p;
+
+	for(p=fhdrlist; p; p=p->next)
+		if(p->pc2file && p->pc2file(p, pc-p->base, file, nfile, line) >= 0)
+			return 0;
+	werrstr("no source file for 0x%lux", pc);
+	return -1;
+}
+
+int
+pc2line(ulong pc, ulong *line)
+{
+	char tmp[10];	/* just in case */
+	return pc2file(pc, tmp, sizeof tmp, line);
+}
+
+int
+file2pc(char *file, ulong line, ulong *addr)
+{
+	Fhdr *p;
+
+	for(p=fhdrlist; p; p=p->next)
+		if(p->file2pc && p->file2pc(p, file, line, addr) >= 0){
+			*addr += p->base;
+			return 0;
+		}
+	werrstr("no instructions at %s:%lud", file, line);
+	return -1;
+}
+
+int
+line2pc(ulong basepc, ulong line, ulong *pc)
+{
+	Fhdr *p;
+
+	for(p=fhdrlist; p; p=p->next)
+		if(p->line2pc && p->line2pc(p, basepc-p->base, line, pc) >= 0){
+			*pc += p->base;
+			return 0;
+		}
+	werrstr("no instructions on line %lud", line);
+	return -1;
+}
+
+int
+fnbound(ulong pc, ulong *bounds)
+{
+	Fhdr *p;
+	Loc l;
+	Symbol *s;
+
+	for(p=fhdrlist; p; p=p->next){
+		l = locaddr(pc - p->base);
+		if((s = ffindsym(p, l, CANY)) != nil){
+			if(s->loc.type != LADDR){
+				werrstr("function %s has weird location %L", s->name, s->loc);
+				return -1;
+			}
+			bounds[0] = s->loc.addr + p->base;
+			if(s->hiloc.type != LADDR){
+				werrstr("can't find upper bound for function %s", s->name);
+				return -1;
+			}
+			bounds[1] = s->hiloc.addr + p->base;
+			return 0;
+		}
+	}
+	werrstr("no function contains 0x%lux", pc);
+	return -1;
+}
+
+int
+fileline(ulong pc, char *a, uint n)
+{
+	ulong line;
+
+	if(pc2file(pc, a, n, &line) < 0)
+		return -1;
+	seprint(a+strlen(a), a+n, ":%lud", line);
+	return 0;
+}
+
+Symbol*
+flookupsym(Fhdr *fhdr, char *name)
+{
+	Symbol **a, *t;
+	uint n, m;
+	int i;
+
+	a = fhdr->byname;
+	n = fhdr->nsym;
+	if(a == nil)
+		return nil;
+
+	while(n > 0){
+		m = n/2;
+		t = a[m];
+		i = strcmp(name, t->name);
+		if(i < 0)
+			n = m;
+		else if(i > 0){
+			n -= m+1;
+			a += m+1;
+		}else{
+			/* found! */
+			m += a - fhdr->byname;
+			a = fhdr->byname;
+			assert(strcmp(name, a[m]->name) == 0);
+			while(m > 0 && strcmp(name, a[m-1]->name) == 0)
+				m--;
+			return a[m];
+		}
+	}
+	return nil;
+}
+
+int
+lookupsym(char *fn, char *var, Symbol *s)
+{
+	Symbol *t, s1;
+	Fhdr *p;
+	char *nam;
+
+	nam = fn ? fn : var;
+	if(nam == nil)
+		return -1;
+	t = nil;
+	for(p=fhdrlist; p; p=p->next)
+		if((t=flookupsym(p, nam)) != nil){
+			relocsym(&s1, t, p->base);
+			break;
+		}
+	if(t == nil)
+		goto err;
+	if(fn && var)
+		return lookuplsym(&s1, var, s);
+	*s = s1;
+	return 0;
+
+err:
+	werrstr("unknown symbol %s%s%s", fn ? fn : "",
+		fn && var ? ":" : "", var ? var : "");
+	return -1;
+}
+
+int
+findexsym(Fhdr *fp, uint i, Symbol *s)
+{
+	if(i >= fp->nsym)
+		return -1;
+	relocsym(s, &fp->sym[i], fp->base);
+	return 0;
+}
+
+int
+indexsym(uint ndx, Symbol *s)
+{
+	uint t;
+	Fhdr *p;
+
+	for(p=fhdrlist; p; p=p->next){
+		t = p->nsym;
+		if(t < ndx)
+			ndx -= t;
+		else{
+			relocsym(s, &p->sym[ndx], p->base);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+Symbol*
+ffindsym(Fhdr *fhdr, Loc loc, uint class)
+{
+	Symbol *a, *t;
+	int n, i, hi, lo;
+	int cmp;
+
+	a = fhdr->sym;
+	n = fhdr->nsym;
+	if(a == nil || n <= 0)
+		return nil;
+
+	/*
+	 * We have a list of possibly duplicate locations in a.
+	 * We want to find the largest index i such that
+	 * a[i] <= loc.  This cannot be done with a simple
+	 * binary search.  Instead we binary search to find
+	 * where the location should be. 
+	 */
+	lo = 0;
+	hi = n;
+	while(lo < hi){
+		i = (lo+hi)/2;
+		cmp = loccmp(&loc, &a[i].loc);
+		if(cmp < 0)	/* loc < a[i].loc */
+			hi = i;
+		if(cmp > 0)	/* loc > a[i].loc */
+			lo = i+1;
+		if(cmp == 0)
+			goto found;
+	}
+
+	/* found position where value would go, but not there -- go back one */
+	if(lo == 0)
+		return nil;
+	i = lo-1;
+
+found:
+	/*
+	 * might be in a run of all-the-same -- go back to beginning of run.
+	 * if runs were long, could binary search for a[i].loc instead.
+	 */
+	while(i > 0 && loccmp(&a[i-1].loc, &a[i].loc) == 0)
+		i--;
+
+	t = &a[i];
+	if(t->hiloc.type && loccmp(&loc, &t->hiloc) >= 0)
+		return nil;
+	if(class != CANY && class != t->class)
+		return nil;
+	return t;
+}
+
+int
+findsym(Loc loc, uint class, Symbol *s)
+{
+	Fhdr *p, *bestp;
+	Symbol *t, *best;
+	long bestd, d;
+	Loc l;
+
+	l = loc;
+	best = nil;
+	bestp = nil;
+	bestd = 0;
+	for(p=fhdrlist; p; p=p->next){
+		if(l.type == LADDR)
+			l.addr = loc.addr - p->base;
+		if((t = ffindsym(p, l, CANY)) != nil){
+			d = l.addr - t->loc.addr;
+			if(d < 4096)
+			if(best == nil || d < bestd){
+				best = t;
+				bestp = p;
+				bestd = d;
+			}
+		}
+	}
+	if(best){
+		if(class != CANY && class != best->class)
+			goto err;
+		relocsym(s, best, bestp->base);
+		return 0;
+	}
+err:
+	werrstr("could not find symbol at %L", loc);
+	return -1;
+}
+
+int
+lookuplsym(Symbol *s1, char *name, Symbol *s2)
+{
+	Fhdr *p;
+
+	p = s1->fhdr;
+	if(p->lookuplsym && p->lookuplsym(p, s1, name, s2) >= 0){
+		relocsym(s2, s2, p->base);
+		return 0;
+	}
+	return -1;
+}
+
+int
+indexlsym(Symbol *s1, uint ndx, Symbol *s2)
+{
+	Fhdr *p;
+
+	p = s1->fhdr;
+	if(p->indexlsym && p->indexlsym(p, s1, ndx, s2) >= 0){
+		relocsym(s2, s2, p->base);
+		return 0;
+	}
+	return -1;
+}
+
+int
+findlsym(Symbol *s1, Loc loc, Symbol *s2)
+{
+	Fhdr *p;
+
+	p = s1->fhdr;
+	if(p->findlsym && p->findlsym(p, s1, loc, s2) >= 0){
+		relocsym(s2, s2, p->base);
+		return 0;
+	}
+	return -1;
+}
+
+int
+unwindframe(Map *map, Regs *regs, ulong *next)
+{
+	Fhdr *p;
+
+	for(p=fhdrlist; p; p=p->next)
+		if(p->unwind && p->unwind(p, map, regs, next) >= 0)
+			return 0;
+	if(mach->unwind && mach->unwind(map, regs, next) >= 0)
+		return 0;
+	return -1;
+}
+
+int
+symoff(char *a, uint n, ulong addr, uint class)
+{
+	Loc l;
+	Symbol s;
+
+	l.type = LADDR;
+	l.addr = addr;
+	if(findsym(l, class, &s) < 0 || addr-s.loc.addr >= 4096){
+		snprint(a, n, "%lux", addr);
+		return -1;
+	}
+	if(addr != s.loc.addr)
+		snprint(a, n, "%s+%ld", s.name, addr-s.loc.addr);
+	else
+		snprint(a, n, "%s", s.name);
+	return 0;
+}
+
+/* location, class, name */
+static int
+byloccmp(const void *va, const void *vb)
+{
+	int i;
+	Symbol *a, *b;
+
+	a = (Symbol*)va;
+	b = (Symbol*)vb;
+	i = loccmp(&a->loc, &b->loc);
+	if(i != 0)
+		return i;
+	i = a->class - b->class;
+	if(i != 0)
+		return i;
+	return strcmp(a->name, b->name);
+}
+
+/* name, location, class */
+static int
+bynamecmp(const void *va, const void *vb)
+{
+	int i;
+	Symbol *a, *b;
+
+	a = *(Symbol**)va;
+	b = *(Symbol**)vb;
+	i = strcmp(a->name, b->name);
+	if(i != 0)
+		return i;
+	i = loccmp(&a->loc, &b->loc);
+	if(i != 0)
+		return i;
+	return a->class - b->class;
+}
+
+int
+syminit(Fhdr *hdr)
+{
+	int i;
+	Symbol *r, *w, *es;
+
+	if(hdr->syminit == nil){
+		werrstr("no debugging symbols");
+		return -1;
+	}
+	if(hdr->syminit(hdr) < 0)
+		return -1;
+
+	qsort(hdr->sym, hdr->nsym, sizeof(hdr->sym[0]), byloccmp);
+	es = hdr->sym+hdr->nsym;
+	for(r=w=hdr->sym; r<es; r++){
+		if(w > hdr->sym
+		&& strcmp((w-1)->name, r->name) ==0
+		&& loccmp(&(w-1)->loc, &r->loc) == 0){
+			/* skip it */
+		}else
+			*w++ = *r;
+	}
+	hdr->nsym = w - hdr->sym;
+
+	hdr->byname = malloc(hdr->nsym*sizeof(hdr->byname[0]));
+	if(hdr->byname == nil){
+		fprint(2, "could not allocate table to sort by location\n");
+	}else{
+		for(i=0; i<hdr->nsym; i++)
+			hdr->byname[i] = &hdr->sym[i];
+		qsort(hdr->byname, hdr->nsym, sizeof(hdr->byname[0]), bynamecmp);
+	}
+	return 0;
+}
+
+Symbol*
+addsym(Fhdr *fp, Symbol *sym)
+{
+	Symbol *s;
+
+	if(fp->nsym%128 == 0){
+		s = realloc(fp->sym, (fp->nsym+128)*sizeof(fp->sym[0]));
+		if(s == nil)
+			return nil;
+		fp->sym = s;
+	}
+	if(machdebug)
+		fprint(2, "sym %s %c %L\n", sym->name, sym->type, sym->loc);
+	sym->fhdr = fp;
+	s = &fp->sym[fp->nsym++];
+	*s = *sym;
+	return s;
+}
+
blob - /dev/null
blob + 8ced09f4478e83933dbe52fc1e2166db3f701789 (mode 644)
--- /dev/null
+++ src/libmach/symdwarf.c
@@ -0,0 +1,466 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+#include "elf.h"
+#include "dwarf.h"
+
+static void	dwarfsymclose(Fhdr*);
+static int	dwarfpc2file(Fhdr*, ulong, char*, uint, ulong*);
+static int	dwarfline2pc(Fhdr*, ulong, ulong, ulong*);
+static int	dwarflookuplsym(Fhdr*, Symbol*, char*, Symbol*);
+static int	dwarfindexlsym(Fhdr*, Symbol*, uint, Symbol*);
+static int	dwarffindlsym(Fhdr*, Symbol*, Loc, Symbol*);
+static void	dwarfsyminit(Fhdr*);
+static int	dwarftosym(Fhdr*, Dwarf*, DwarfSym*, Symbol*, int);
+static int	_dwarfunwind(Fhdr *fhdr, Map *map, Regs *regs, ulong *next);
+
+int
+symdwarf(Fhdr *hdr)
+{
+	if(hdr->dwarf == nil){
+		werrstr("no dwarf debugging symbols");
+		return -1;
+	}
+
+	hdr->symclose = dwarfsymclose;
+	hdr->pc2file = dwarfpc2file;
+	hdr->line2pc = dwarfline2pc;
+	hdr->lookuplsym = dwarflookuplsym;
+	hdr->indexlsym = dwarfindexlsym;
+	hdr->findlsym = dwarffindlsym;
+	hdr->unwind = _dwarfunwind;
+	dwarfsyminit(hdr);
+
+	return 0;
+}
+
+static void
+dwarfsymclose(Fhdr *hdr)
+{
+	dwarfclose(hdr->dwarf);
+	hdr->dwarf = nil;
+}
+
+static int
+dwarfpc2file(Fhdr *fhdr, ulong pc, char *buf, uint nbuf, ulong *line)
+{
+	char *cdir, *dir, *file;
+
+	if(dwarfpctoline(fhdr->dwarf, pc, &cdir, &dir, &file, line, nil, nil) < 0)
+		return -1;
+
+	if(file[0] == '/' || (dir==nil && cdir==nil))
+		strecpy(buf, buf+nbuf, file);
+	else if((dir && dir[0] == '/') || cdir==nil)
+		snprint(buf, nbuf, "%s/%s", dir, file);
+	else
+		snprint(buf, nbuf, "%s/%s/%s", cdir, dir ? dir : "", file);
+	cleanname(buf);
+	return 0;;
+}
+
+static int
+dwarfline2pc(Fhdr *fhdr, ulong basepc, ulong line, ulong *pc)
+{
+	werrstr("dwarf line2pc not implemented");
+	return -1;
+}
+
+static uint
+typesize(Dwarf *dwarf, ulong unit, ulong tref, char *name)
+{
+	DwarfSym ds;
+
+top:
+	if(dwarfseeksym(dwarf, unit, tref-unit, &ds) < 0){
+	cannot:
+		fprint(2, "warning: cannot compute size of parameter %s (%lud %lud: %r)\n",
+			name, unit, tref);
+		return 0;
+	}
+	
+	if(ds.attrs.have.bytesize)
+		return ds.attrs.bytesize;
+
+	switch(ds.attrs.tag){
+	case TagVolatileType:
+	case TagRestrictType:
+	case TagTypedef:
+		if(ds.attrs.have.type != TReference)
+			goto cannot;
+		tref = ds.attrs.type;
+		goto top;
+	}
+
+	goto cannot;
+}
+
+static int
+roundup(int s, int n)
+{
+	return (s+n-1)&~(n-1);
+}
+
+static int
+dwarflenum(Fhdr *fhdr, Symbol *p, char *name, uint j, Loc l, Symbol *s)
+{
+	int depth, bpoff;
+	DwarfSym ds;
+	Symbol s1;
+
+	if(p == nil)
+		return -1;
+
+	if(dwarfseeksym(fhdr->dwarf, p->u.dwarf.unit, p->u.dwarf.uoff, &ds) < 0)
+		return -1;
+
+	ds.depth = 1;
+	depth = 1;
+
+	bpoff = 8;
+	while(dwarfnextsym(fhdr->dwarf, &ds, 1) == 1 && depth < ds.depth){
+		if(ds.attrs.tag != TagVariable){
+			if(ds.attrs.tag != TagFormalParameter
+			&& ds.attrs.tag != TagUnspecifiedParameters)
+				continue;
+			if(ds.depth != depth+1)
+				continue;
+		}
+		if(dwarftosym(fhdr, fhdr->dwarf, &ds, &s1, 1) < 0)
+			continue;
+		/* XXX move this out once there is another architecture */
+		/*
+		 * gcc tells us the registers where the parameters might be
+		 * held for an instruction or two.  use the parameter list to
+		 * recompute the actual stack locations.
+		 */
+		if(fhdr->mtype == M386)
+		if(ds.attrs.tag==TagFormalParameter || ds.attrs.tag==TagUnspecifiedParameters){
+			if(s1.loc.type==LOFFSET
+			&& strcmp(s1.loc.reg, "BP")==0
+			&& s1.loc.offset >= 8)
+				bpoff = s1.loc.offset;
+			else{
+				s1.loc.type = LOFFSET;
+				s1.loc.reg = "BP";
+				s1.loc.offset = bpoff;
+			}
+			if(ds.attrs.tag == TagFormalParameter){
+				if(ds.attrs.have.type)
+					bpoff += roundup(typesize(fhdr->dwarf, p->u.dwarf.unit, ds.attrs.type, s1.name), 4);
+				else
+					fprint(2, "warning: cannot compute size of parameter %s\n", s1.name);
+			}
+		}
+		if(name){
+			if(strcmp(ds.attrs.name, name) != 0)
+				continue;
+		}else if(l.type){
+			if(loccmp(&s1.loc, &l) != 0)
+				continue;
+		}else{
+			if(j-- > 0)
+				continue;
+		}
+		*s = s1;
+		return 0;
+	}
+	return -1;
+}
+
+static Loc zl;
+
+static int
+dwarflookuplsym(Fhdr *fhdr, Symbol *p, char *name, Symbol *s)
+{
+	return dwarflenum(fhdr, p, name, 0, zl, s);
+}
+
+static int
+dwarfindexlsym(Fhdr *fhdr, Symbol *p, uint i, Symbol *s)
+{
+	return dwarflenum(fhdr, p, nil, i, zl, s);
+}
+
+static int
+dwarffindlsym(Fhdr *fhdr, Symbol *p, Loc l, Symbol *s)
+{
+	return dwarflenum(fhdr, p, nil, 0, l, s);
+}
+
+static void
+dwarfsyminit(Fhdr *fp)
+{
+	Dwarf *d;
+	DwarfSym s;
+	Symbol sym;
+
+	d = fp->dwarf;
+	if(dwarfenum(d, &s) < 0)
+		return;
+
+	while(dwarfnextsym(d, &s, s.depth!=1) == 1){
+		if(s.depth != 1)
+			continue;
+		if(s.attrs.name == nil)
+			continue;
+		switch(s.attrs.tag){
+		case TagSubprogram:
+		case TagVariable:
+			if(dwarftosym(fp, d, &s, &sym, 0) < 0)
+				continue;
+			addsym(fp, &sym);
+		}
+	}
+}
+
+static char*
+regname(Dwarf *d, int i)
+{
+	if(i < 0 || i >= d->nreg)
+		return nil;
+	return d->reg[i];
+}
+
+static int
+dwarftosym(Fhdr *fp, Dwarf *d, DwarfSym *ds, Symbol *s, int infn)
+{
+	DwarfBuf buf;
+	DwarfBlock b;
+	
+	memset(s, 0, sizeof *s);
+	s->u.dwarf.uoff = ds->uoff;
+	s->u.dwarf.unit = ds->unit;
+	switch(ds->attrs.tag){
+	default:
+		return -1;
+	case TagUnspecifiedParameters:
+		ds->attrs.name = "...";
+		s->type = 'p';
+		goto sym;
+	case TagFormalParameter:
+		s->type = 'p';
+		s->class = CPARAM;
+		goto sym;
+	case TagSubprogram:
+		s->type = 't';
+		s->class = CTEXT;
+		goto sym;
+	case TagVariable:
+		if(infn){
+			s->type = 'a';
+			s->class = CAUTO;
+		}else{
+			s->type = 'd';
+			s->class = CDATA;
+		}
+	sym:
+		s->name = ds->attrs.name;
+		if(ds->attrs.have.lowpc){
+			s->loc.type = LADDR;
+			s->loc.addr = ds->attrs.lowpc;
+			if(ds->attrs.have.highpc){
+				s->hiloc.type = LADDR;
+				s->hiloc.addr = ds->attrs.highpc;
+			}
+		}else if(ds->attrs.have.location == TConstant){
+			s->loc.type = LADDR;
+			s->loc.addr = ds->attrs.location.c;
+		}else if(ds->attrs.have.location == TBlock){
+			b = ds->attrs.location.b;
+			if(b.len == 0)
+				return -1;
+			buf.p = b.data+1;
+			buf.ep = b.data+b.len;
+			buf.d = d;
+			buf.addrsize = 0;
+			if(b.data[0]==OpAddr){
+				if(b.len != 5)
+					return -1;
+				s->loc.type = LADDR;
+				s->loc.addr = dwarfgetaddr(&buf);
+			}else if(OpReg0 <= b.data[0] && b.data[0] < OpReg0+0x20){
+				if(b.len != 1 || (s->loc.reg = regname(d, b.data[0]-OpReg0)) == nil)
+					return -1;
+				s->loc.type = LREG;
+			}else if(OpBreg0 <= b.data[0] && b.data[0] < OpBreg0+0x20){
+				s->loc.type = LOFFSET;
+				s->loc.reg = regname(d, b.data[0]-0x70);
+				s->loc.offset = dwarfget128s(&buf);
+				if(s->loc.reg == nil)
+					return -1;
+			}else if(b.data[0] == OpRegx){
+				s->loc.type = LREG;
+				s->loc.reg = regname(d, dwarfget128(&buf));
+				if(s->loc.reg == nil)
+					return -1;
+			}else if(b.data[0] == OpFbreg){
+				s->loc.type = LOFFSET;
+				s->loc.reg = mach->fp;
+				s->loc.offset = dwarfget128s(&buf);
+			}else if(b.data[0] == OpBregx){
+				s->loc.type = LOFFSET;
+				s->loc.reg = regname(d, dwarfget128(&buf));
+				s->loc.offset = dwarfget128s(&buf);
+				if(s->loc.reg == nil)
+					return -1;
+			}else
+				s->loc.type = LNONE;
+			if(buf.p != buf.ep)
+				s->loc.type = LNONE;
+		}else
+			return -1;
+		if(ds->attrs.isexternal)
+			s->type += 'A' - 'a';
+		if(ds->attrs.tag==TagVariable && s->loc.type==LADDR && s->loc.addr>=fp->dataddr+fp->datsz)
+			s->type += 'b' - 'd';
+		s->fhdr = fp;
+		return 0;
+	}
+}
+
+static int
+dwarfeval(Dwarf *d, Map *map, Regs *regs, ulong cfa, int rno, DwarfExpr e, ulong *u)
+{
+	int i;
+	u32int u4;
+	ulong uu;
+
+	switch(e.type){
+	case RuleUndef:
+		*u = 0;
+		return 0;
+	case RuleSame:
+		if(rno == -1){
+			werrstr("pc cannot be `same'");
+			return -1;
+		}
+		return rget(regs, regname(d, rno), u);
+	case RuleRegister:
+		if((i = windindex(regname(d, e.reg))) < 0)
+			return -1;
+		return rget(regs, regname(d, i), u);
+	case RuleCfaOffset:
+		if(cfa == 0){
+			werrstr("unknown cfa");
+			return -1;
+		}
+		if(get4(map, cfa + e.offset, &u4) < 0)
+			return -1;
+		*u = u4;
+		return 0;
+	case RuleRegOff:
+		if(rget(regs, regname(d, e.reg), &uu) < 0)
+			return -1;
+		if(get4(map, uu+e.offset, &u4) < 0)
+			return -1;	
+		*u = u4;
+		return 0;
+	case RuleLocation:
+		werrstr("not evaluating dwarf loc expressions");
+		return -1;
+	}
+	werrstr("not reached in dwarfeval");
+	return -1;
+}
+
+#if 0
+static int
+dwarfexprfmt(Fmt *fmt)
+{
+	DwarfExpr *e;
+
+	if((e = va_arg(fmt->args, DwarfExpr*)) == nil)
+		return fmtstrcpy(fmt, "<nil>");
+
+	switch(e->type){
+	case RuleUndef:
+		return fmtstrcpy(fmt, "undef");
+	case RuleSame:
+		return fmtstrcpy(fmt, "same");
+	case RuleCfaOffset:
+		return fmtprint(fmt, "%ld(cfa)", e->offset);
+	case RuleRegister:
+		return fmtprint(fmt, "r%ld", e->reg);
+	case RuleRegOff:
+		return fmtprint(fmt, "%ld(r%ld)", e->offset, e->reg);
+	case RuleLocation:
+		return fmtprint(fmt, "l.%.*H", e->loc.len, e->loc.data);
+	default:
+		return fmtprint(fmt, "?%d", e->type);
+	}
+}
+#endif
+
+static int
+_dwarfunwind(Fhdr *fhdr, Map *map, Regs *regs, ulong *next)
+{
+	char *name;
+	int i, j;
+	ulong cfa, pc, u;
+	Dwarf *d;
+	DwarfExpr *e, epc, ecfa;
+
+
+	/*
+	 * Use dwarfunwind to tell us what to do.
+	 */
+	d = fhdr->dwarf;
+	e = malloc(d->nreg*sizeof(e[0]));
+	if(e == nil)
+		return -1;
+	if(rget(regs, mach->pc, &pc) < 0)
+		goto err;
+	if(dwarfunwind(d, pc, &ecfa, &epc, e, d->nreg) < 0)
+		goto err;
+
+	/*
+	 * Compute CFA.
+	 */
+	switch(ecfa.type){
+	default:
+		werrstr("invalid call-frame-address in _dwarfunwind");
+		goto err;
+	case RuleRegister:
+		ecfa.offset = 0;
+	case RuleRegOff:
+		if((name = regname(d, ecfa.reg)) == nil){
+			werrstr("invalid call-frame-address register %d", (int)ecfa.reg);
+			goto err;
+		}
+		if(rget(regs, name, &cfa) < 0){
+			werrstr("fetching %s for call-frame-address: %r", name);
+			goto err;
+		}
+		cfa += ecfa.offset;
+	}
+
+	/*
+	 * Compute registers.
+	 */
+	for(i=0; i<d->nreg; i++){
+		j = windindex(d->reg[i]);
+		if(j == -1)
+			continue;
+		if(dwarfeval(d, map, regs, cfa, i, e[i], &u) < 0)
+			u = ~(ulong)0;
+		next[j] = u;
+	}
+
+	/*
+	 * Compute caller pc
+	 */
+	if(dwarfeval(d, map, regs, cfa, -1, epc, &u) < 0){
+		werrstr("computing caller %s: %r", mach->pc);
+		goto err;
+	}
+	next[windindex(mach->pc)] = u;
+	free(e);
+	return 0;
+
+err:
+	free(e);
+	return -1;
+}
+
blob - /dev/null
blob + 7babbd496997724635fa29c7c29951b6a8e26eb5 (mode 644)
--- /dev/null
+++ src/libmach/symelf.c
@@ -0,0 +1,103 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "elf.h"
+
+static int
+elfsyminit(Fhdr *fp)
+{
+	int i, onlyundef;
+	Elf *elf;
+	Symbol sym;
+	ElfSym esym;
+	ElfProg *p;
+
+	elf = fp->elf;
+
+	onlyundef = fp->nsym > 0;
+	for(i=0; elfsym(elf, i, &esym) >= 0; i++){
+		if(esym.name == nil)
+			continue;
+		if(onlyundef && esym.shndx != ElfSymShnNone)
+			continue;
+		if(esym.type != ElfSymTypeObject && esym.type != ElfSymTypeFunc)
+			continue;
+		if(strchr(esym.name, '@'))
+			continue;
+		memset(&sym, 0, sizeof sym);
+		sym.name = esym.name;
+		sym.loc.type = LADDR;
+		sym.loc.addr = esym.value;
+		if(esym.size){
+			sym.hiloc.type = LADDR;
+			sym.hiloc.addr = esym.value+esym.size;
+		}
+		sym.fhdr = fp;
+		if(esym.type==ElfSymTypeObject){
+			sym.class = CDATA;
+			sym.type = 'D';
+			if(&elf->sect[esym.shndx] == elf->bss)
+				sym.type = 'B';
+		}else if(esym.type==ElfSymTypeFunc){
+			sym.class = CTEXT;
+			sym.type = 'T';
+		}
+		if(esym.shndx == ElfSymShnNone)
+			sym.type = 'U';
+		if(esym.bind==ElfSymBindLocal)
+			sym.type += 'a' - 'A';
+		addsym(fp, &sym);
+	}
+
+	for(i=0; i<elf->nprog; i++){
+		p = &elf->prog[i];
+		if(p->type != ElfProgDynamic)
+			continue;
+		memset(&sym, 0, sizeof sym);
+		sym.name = "_DYNAMIC";
+		sym.loc = locaddr(p->vaddr);
+		sym.hiloc = locaddr(p->vaddr+p->filesz);
+		sym.type = 'D';
+		sym.class = CDATA;
+		addsym(fp, &sym);
+	}
+	return 0;
+}
+
+int
+symelf(Fhdr *fhdr)
+{
+	int ret;
+
+	ret = -1;
+
+	/* try dwarf */
+	if(fhdr->dwarf){
+		if(machdebug)
+			fprint(2, "dwarf symbols...\n");
+		if(symdwarf(fhdr) < 0)
+			fprint(2, "initializing dwarf: %r");
+		else
+			ret = 0;
+	}
+
+	/* try stabs */
+	if(fhdr->stabs.stabbase){
+		if(machdebug)
+			fprint(2, "stabs symbols...\n");
+		if(symstabs(fhdr) < 0)
+			fprint(2, "initializing stabs: %r");
+		else
+			ret = 0;
+	}
+
+	if(machdebug)
+		fprint(2, "elf symbols...\n");
+
+	if(elfsyminit(fhdr) < 0)
+		fprint(2, "initializing elf: %r");
+	else
+		ret = 0;
+	return ret;
+}
+
blob - /dev/null
blob + 674190ffe4e0c63e596bf6476b17a30182344a6d (mode 644)
--- /dev/null
+++ src/libmach/symmacho.c
@@ -0,0 +1,50 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "macho.h"
+
+#if 0
+static int
+machosyminit(Fhdr *fp)
+{
+	/* XXX should parse dynamic symbol table here */
+	return 0;
+}
+#endif
+
+int
+symmacho(Fhdr *fp)
+{
+	int ret;
+	Macho *m;
+
+	m = fp->macho;
+	if(m == nil){
+		werrstr("not a macho");
+		return -1;
+	}
+
+	ret = -1;
+
+	if(machdebug)
+		fprint(2, "macho symbols...\n");
+
+/*
+	if(machosyminit(fp) < 0)
+		fprint(2, "initializing macho symbols: %r\n");
+	else
+		ret = 0;
+*/
+
+	if(fp->stabs.stabbase){
+		if(machdebug)
+			fprint(2, "stabs symbols...\n");
+
+		if(symstabs(fp) < 0)
+			fprint(2, "initializing stabs: %r");
+		else
+			ret = 0;
+	}
+	return ret;
+}
+
blob - /dev/null
blob + 8c1ddac8d84ca5dcb5fb6ef0d73360db7030bcfb (mode 644)
--- /dev/null
+++ src/libmach/symstabs.c
@@ -0,0 +1,401 @@
+#include <u.h>
+#include <libc.h>
+#include <mach.h>
+#include "stabs.h"
+
+static int
+strcmpcolon(char *a, char *bcolon)
+{
+	int i, len;
+	char *p;
+
+	p = strchr(bcolon, ':');
+	if(p == nil)
+		return strcmp(a, bcolon);
+	len = p-bcolon;
+	i = strncmp(a, bcolon, len);
+	if(i)
+		return i;
+	if(a[len] == 0)
+		return 0;
+	return 1;
+}
+
+static int
+stabcvtsym(StabSym *stab, Symbol *sym, char *dir, char *file, int i)
+{
+	char *p;
+
+	/*
+	 * Zero out the : to avoid allocating a new name string.
+	 * The type info can be found by looking past the NUL.
+	 * This is going to get us in trouble...
+	 */
+	if((p = strchr(stab->name, ':')) != nil)
+		*p++ = 0;
+	else
+		p = stab->name+strlen(stab->name)+1;
+
+	sym->name = stab->name;
+	sym->u.stabs.dir = dir;
+	sym->u.stabs.file = file;
+	sym->u.stabs.i = i;
+	switch(stab->type){
+	default:
+		return -1;
+	case N_FUN:
+		sym->class = CTEXT;
+		switch(*p){
+		default:
+			return -1;
+		case 'F':	/* global function */
+			sym->type = 'T';
+			break;
+		case 'Q':	/* static procedure */
+		case 'f':	/* static function */
+		case 'I':	/* nested procedure */
+		case 'J':	/* nested function */
+			sym->type = 't';
+			break;
+		}
+		sym->loc.type = LADDR;
+		sym->loc.addr = stab->value;
+		break;
+	case N_GSYM:
+	case N_PSYM:
+	case N_LSYM:
+	case N_LCSYM:
+		sym->class = CDATA;
+		sym->loc.type = LADDR;
+		sym->loc.addr = stab->value;
+		switch(*p){
+		default:
+			return -1;
+		case 'S':	/* file-scope static variable */
+			sym->type = 'd';
+			break;
+		case 'G':	/* global variable */
+			sym->type = 'D';
+			sym->loc.type = LNONE;
+			break;
+		case 'r':	/* register variable */
+			sym->class = CAUTO;
+			sym->type = 'a';
+			sym->loc.type = LREG;
+			sym->loc.reg = "XXX";
+			break;
+		case 's':	/* local variable */
+			sym->class = CAUTO;
+			sym->type = 'a';
+			sym->loc.type = LOFFSET;
+			sym->loc.offset = stab->value;
+			sym->loc.reg = "XXX";
+			break;
+		case 'a':	/* by reference */
+		case 'D':	/* f.p. parameter */
+		case 'i':	/* register parameter */
+		case 'p':	/* "normal" parameter */
+		case 'P':	/* register parameter */
+		case 'v':	/* by reference */
+		case 'X':	/* function return variable */
+			sym->class = CPARAM;
+			sym->type = 'p';
+			if(*p == 'i'){
+				sym->loc.type = LREG;
+				sym->loc.reg = "XXX";
+			}else{
+				sym->loc.type = LOFFSET;
+				sym->loc.offset = stab->value;
+				sym->loc.reg = "XXX";
+			}
+			break;
+		}
+		break;
+	}
+	return 0;	
+}
+
+static int
+stabssyminit(Fhdr *fp)
+{
+	int i;
+	char *dir, *file;
+	Stab *stabs;
+	StabSym sym, lastfun;
+	Symbol s, *fun;
+	char **inc, **xinc;
+	int ninc, minc;
+	int locals, autos, params;
+
+	stabs = &fp->stabs;
+	if(stabs == nil){
+		werrstr("no stabs info");
+		return -1;
+	}
+
+	dir = nil;
+	file = nil;
+	inc = nil;
+	fun = nil;
+	ninc = 0;
+	minc = 0;
+	locals = 0;
+	params = 0;
+	autos = 0;
+	memset(&lastfun, 0, sizeof lastfun);
+	for(i=0; stabsym(stabs, i, &sym)>=0; i++){
+		switch(sym.type){
+		case N_SO:
+			if(sym.name == nil || *sym.name == 0){
+				file = nil;
+				break;
+			}
+			if(sym.name[strlen(sym.name)-1] == '/')
+				dir = sym.name;
+			else
+				file = sym.name;
+			break;
+		case N_BINCL:
+			if(ninc >= minc){
+				xinc = realloc(inc, (ninc+32)*sizeof(inc[0]));
+				if(xinc){
+					memset(xinc+ninc, 0, 32*sizeof(inc[0]));
+					inc = xinc;
+				}
+				ninc += 32;
+			}
+			if(ninc < minc)
+				inc[ninc] = sym.name;
+			ninc++;
+			break;
+		case N_EINCL:
+			if(ninc > 0)
+				ninc--;
+			break;
+		case N_EXCL:
+			/* condensed include - same effect as previous BINCL/EINCL pair */
+			break;
+		case N_GSYM:	/* global variable */
+			/* only includes type, so useless for now */
+			break;
+		case N_FUN:
+			if(sym.name == nil){
+				/* marks end of function */
+				if(fun){
+					fun->hiloc.type = LADDR;
+					fun->hiloc.addr = fun->loc.addr + sym.value;
+				}
+				break;
+			}
+			if(fun && lastfun.value==sym.value && lastfun.name==sym.name){
+				fun->u.stabs.locals = i;
+				break;
+			}
+			/* create new symbol, add it */
+			lastfun = sym;
+			fun = nil;
+			if(stabcvtsym(&sym, &s, dir, file, i) < 0)
+				continue;
+			if((fun = addsym(fp, &s)) == nil)
+				goto err;
+			locals = 0;
+			params = 0;
+			autos = 0;
+			break;
+		case N_PSYM:
+		case N_LSYM:
+		case N_LCSYM:
+			if(fun){
+				if(fun->u.stabs.frameptr == -1){
+					/*
+					 * Try to distinguish functions with a real frame pointer
+				 	 * from functions with a virtual frame pointer, based on 
+					 * whether the first parameter is in the right location and
+					 * whether the autos have negative offsets.  
+					 * 
+					 * This heuristic works most of the time.  On the 386, we
+					 * cannot distinguish between a v. function with no autos
+					 * but a frame of size 4 and a f.p. function with no autos and
+					 * no frame.   Anything else we'll get right.
+					 * 
+					 * Another way to go about this would be to have
+					 * mach-specific functions to inspect the function
+					 * prologues when we're not sure.  What we have
+					 * already should be enough, though.
+					 */
+					if(params==0 && sym.type == N_PSYM){
+						if(sym.value != 8 && sym.value >= 4){
+							/* XXX 386 specific, but let's find another system before generalizing */
+							fun->u.stabs.frameptr = 0;
+							fun->u.stabs.framesize = sym.value - 4;
+						}
+					}else if(sym.type == N_LSYM){
+						if(sym.value >= 0){
+							fun->u.stabs.frameptr = 0;
+							if(params)
+								fun->u.stabs.framesize = 8 - 4;
+						}else
+							fun->u.stabs.frameptr = 1;
+					}
+				}
+				if(sym.type == N_PSYM)
+					params++;
+				if(sym.type == N_LSYM)
+					autos++;
+			}
+			break;
+
+		case N_STSYM:	/* static file-scope variable */
+			/* create new symbol, add it */
+			if(stabcvtsym(&sym, &s, dir, file, i) < 0)
+				continue;
+			if(addsym(fp, &s) < 0)
+				goto err;
+			break;
+		}
+	}
+	free(inc);
+	return 0;
+
+err:
+	free(inc);
+	return -1;
+}
+
+static int
+stabspc2file(Fhdr *fhdr, ulong pc, char *buf, uint nbuf, ulong *pline)
+{
+	int i;
+	Symbol *s;
+	StabSym ss;
+	ulong line, basepc;
+	Loc l;
+
+	l.type = LADDR;
+	l.addr = pc;
+	if((s = ffindsym(fhdr, l, CTEXT)) == nil
+	|| stabsym(&fhdr->stabs, s->u.stabs.i, &ss) < 0)
+		return -1;
+
+	line = ss.desc;
+	basepc = ss.value;
+	for(i=s->u.stabs.i+1; stabsym(&fhdr->stabs, i, &ss) >= 0; i++){
+		if(ss.type == N_FUN && ss.name == nil)
+			break;
+		if(ss.type == N_SLINE){
+			if(basepc+ss.value > pc)
+				break;
+			else
+				line = ss.desc;
+		}
+	}
+	*pline = line;
+	if(s->u.stabs.dir)
+		snprint(buf, nbuf, "%s%s", s->u.stabs.dir, s->u.stabs.file);
+	else
+		snprint(buf, nbuf, "%s", s->u.stabs.file);
+	return 0;
+}
+
+static int
+stabsline2pc(Fhdr *fhdr, ulong startpc, ulong line, ulong *pc)
+{
+	int i, trigger;
+	Symbol *s;
+	StabSym ss;
+	ulong basepc;
+	Loc l;
+
+	l.type = LADDR;
+	l.addr = startpc;
+	if((s = ffindsym(fhdr, l, CTEXT)) == nil)
+		return -1;
+
+	trigger = 0;
+	line = ss.desc;
+	basepc = ss.value;
+	for(i=s->u.stabs.i+1; stabsym(&fhdr->stabs, i, &ss) >= 0; i++){
+		if(ss.type == N_FUN)
+			basepc = ss.value;
+		if(ss.type == N_SLINE){
+			if(basepc+ss.value >= startpc)
+				trigger = 1;
+			if(trigger && ss.desc >= line){
+				*pc = basepc+ss.value;
+				return 0;
+			}
+		}
+	}
+	return -1;
+}
+
+static int
+stabslenum(Fhdr *fhdr, Symbol *p, char *name, uint j, Loc l, Symbol *s)
+{
+	int i;
+	StabSym ss;
+
+	for(i=p->u.stabs.locals; stabsym(&fhdr->stabs, i, &ss)>=0; i++){
+		if(ss.type == N_FUN && ss.name == nil)
+			break;
+		switch(ss.type){
+		case N_PSYM:
+		case N_LSYM:
+		case N_LCSYM:
+			if(name){
+				if(strcmpcolon(name, ss.name) != 0)
+					break;
+			}else if(l.type){
+				/* wait for now */
+			}else{
+				if(j-- > 0)
+					break;
+			}
+			if(stabcvtsym(&ss, s, p->u.stabs.dir, p->u.stabs.file, i) < 0)
+				return -1;
+			if(s->loc.type == LOFFSET){
+				if(p->u.stabs.frameptr == 0)
+					s->loc.reg = mach->sp;
+				else
+					s->loc.reg = mach->fp;
+			}
+			if(l.type && loccmp(&l, &s->loc) != 0)
+				break;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static Loc zl;
+
+static int
+stabslookuplsym(Fhdr *fhdr, Symbol *p, char *name, Symbol *s)
+{
+	return stabslenum(fhdr, p, name, 0, zl, s);
+}
+
+static int
+stabsindexlsym(Fhdr *fhdr, Symbol *p, uint i, Symbol *s)
+{
+	return stabslenum(fhdr, p, nil, i, zl, s);
+}
+
+static int
+stabsfindlsym(Fhdr *fhdr, Symbol *p, Loc l, Symbol *s)
+{
+	return stabslenum(fhdr, p, nil, 0, l, s);
+}
+
+int
+symstabs(Fhdr *fp)
+{
+	if(stabssyminit(fp) < 0)
+		return -1;
+	fp->pc2file = stabspc2file;
+	fp->line2pc = stabsline2pc;
+	fp->lookuplsym = stabslookuplsym;
+	fp->indexlsym = stabsindexlsym;
+	fp->findlsym = stabsfindlsym;
+	return 0;
+}
blob - /dev/null
blob + 961ef6d093d7b18d1c04e721dfdfbde3294a6c72 (mode 644)
--- /dev/null
+++ src/libmach/ureg386.h
@@ -0,0 +1,45 @@
+typedef struct Ureg Ureg;
+struct Ureg
+{
+	ulong	di;		/* general registers */
+	ulong	si;		/* ... */
+	ulong	bp;		/* ... */
+	ulong	nsp;
+	ulong	bx;		/* ... */
+	ulong	dx;		/* ... */
+	ulong	cx;		/* ... */
+	ulong	ax;		/* ... */
+	ulong	gs;		/* data segments */
+	ulong	fs;		/* ... */
+	ulong	es;		/* ... */
+	ulong	ds;		/* ... */
+	ulong	trap;		/* trap type */
+	ulong	ecode;		/* error code (or zero) */
+	ulong	pc;		/* pc */
+	ulong	cs;		/* old context */
+	ulong	flags;		/* old flags */
+	ulong	sp;
+	ulong	ss;		/* old stack segment */
+};
+
+typedef struct UregLinux386 UregLinux386;
+struct UregLinux386
+{
+	ulong	ebx;
+	ulong	ecx;
+	ulong	edx;
+	ulong	esi;
+	ulong	ebp;
+	ulong	eax;
+	ulong	xds;
+	ulong	xes;
+	ulong	xfs;
+	ulong	xgs;
+	ulong	origeax;
+	ulong	eip;
+	ulong	xcs;
+	ulong	eflags;
+	ulong	esp;
+	ulong	xss;
+};
+
blob - /dev/null
blob + 0e98d19d6817a83ebdea9b08153db79d533c721c (mode 644)
--- /dev/null
+++ src/libmach/uregpower.h
@@ -0,0 +1,54 @@
+typedef struct Ureg Ureg;
+
+struct Ureg
+{
+/*  0*/	ulong	cause;
+/*  4*/	ulong	srr1;	/* aka status */
+/*  8*/	ulong	pc;	/* SRR0 */
+/* 12*/	ulong	pad;
+/* 16*/	ulong	lr;
+/* 20*/	ulong	cr;
+/* 24*/	ulong	xer;
+/* 28*/	ulong	ctr;
+/* 32*/	ulong	r0;
+/* 36*/	ulong	r1;	/* aka sp */
+/* 40*/	ulong	r2;
+/* 44*/	ulong	r3;
+/* 48*/	ulong	r4;
+/* 52*/	ulong	r5;
+/* 56*/	ulong	r6;
+/* 60*/	ulong	r7;
+/* 64*/	ulong	r8;
+/* 68*/	ulong	r9;
+/* 72*/	ulong	r10;
+/* 76*/	ulong	r11;
+/* 80*/	ulong	r12;
+/* 84*/	ulong	r13;
+/* 88*/	ulong	r14;
+/* 92*/	ulong	r15;
+/* 96*/	ulong	r16;
+/*100*/	ulong	r17;
+/*104*/	ulong	r18;
+/*108*/	ulong	r19;
+/*112*/	ulong	r20;
+/*116*/	ulong	r21;
+/*120*/	ulong	r22;
+/*124*/	ulong	r23;
+/*128*/	ulong	r24;
+/*132*/	ulong	r25;
+/*136*/	ulong	r26;
+/*140*/	ulong	r27;
+/*144*/	ulong	r28;
+/*148*/	ulong	r29;
+/*152*/	ulong	r30;
+/*156*/	ulong	r31;
+/*160*/	ulong	dcmp;
+/*164*/	ulong	icmp;
+/*168*/	ulong	dmiss;
+/*172*/	ulong	imiss;
+/*176*/	ulong	hash1;
+/*180*/	ulong	hash2;
+/*184*/	ulong	dar;
+/*188*/	ulong	dsisr;
+/*192*/	ulong	vrsave;
+};