shithub: 5v

Download patch

ref: f9198297ea2bc1c3971f5b3fd473e45a8fb0d10c
author: ori <ori@cleaver>
date: Mon Jan 5 19:10:02 EST 2009

5v: initial commit

--- /dev/null
+++ b/5e.c
@@ -1,0 +1,138 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+int vfp = 1;
+int nflag, pflag, bflag;
+int check;
+u32int mallocaddr, freeaddr, reallocaddr;
+Ref nproc;
+
+void
+dump(void)
+{
+	int i;
+	
+	for(i = 0; i < 16; i++) {
+		print("R%2d %.8ux", i, P->R[i]);
+		if((i % 4) == 3) print("\n");
+		else print("\t");
+	}
+}
+
+static void
+adjustns(void)
+{
+	if(bind("/arm/bin", "/bin", MREPL) == -1)
+		sysfatal("bind: %r");
+	if(bind("/rc/bin", "/bin", MAFTER) == -1)
+		sysfatal("bind: %r");
+	putenv("cputype", "arm");
+	putenv("objtype", "arm");
+}
+
+void
+cleanup(void)
+{
+	if(P == nil)
+		return;
+
+	clrex();
+	remproc(P);
+	decref(&nproc);
+	freesegs();
+	fddecref(P->fd);
+	if(P->path != nil && decref(P->path) == 0)
+		free(P->path);
+	free(P);
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: 5e [-npbF] text [...]\n");
+	exits(nil);
+}
+
+void
+suicide(char *fmt, ...)
+{
+	va_list va;
+	char buf[1024];
+	
+	va_start(va, fmt);
+	vsnprint(buf, sizeof(buf), fmt, va);
+	va_end(va);
+	fprint(2, "%s\n", buf);
+	if(!bflag)
+		exits(buf);
+	abort();
+}
+
+int
+notehandler(void *, char *note)
+{
+	if(strncmp(note, "sys:", 4) == 0)
+		return 0;
+	
+	if(strncmp(note, "emu:", 4) == 0)
+		exits(note);
+
+	addnote(note);
+	return 1;
+}
+
+static void
+dotext(int argc, char **argv)
+{
+	char *file;
+
+	if(**argv == '/' || **argv == '.' || **argv == '#') {
+		if(loadtext(*argv, argc, argv) < 0)
+			sysfatal("loadtext: %r");
+		return;
+	}
+	file = smprint("/bin/%s", *argv);
+	if(loadtext(file, argc, argv) < 0)
+		sysfatal("loadtext: %r");
+	free(file);
+}
+
+void
+main(int argc, char **argv)
+{
+	ARGBEGIN {
+	case 'n': nflag++; break;
+	case 'p': pflag++; break;
+	case 'b': bflag++; break;
+	case 'f': vfp = 1; break;
+	case 'F': vfp = 0; break;
+	case 'c': check++; break;
+	default: usage();
+	} ARGEND;
+	if(argc < 1)
+		usage();
+	if(_nprivates < 1)
+		sysfatal("we don't have privates");
+	if(rfork(RFREND | RFNAMEG | RFENVG) < 0)
+		sysfatal("rfork: %r");
+	atexit(cleanup);
+	if(!nflag)
+		adjustns();
+	if(pflag)
+		initfs("armproc", "/proc");
+	initproc();
+	dotext(argc, argv);
+	atnotify(notehandler, 1);
+	for(;;) {
+		if(ultraverbose)
+			dump();
+		step();
+		while((P->notein - P->noteout) % NNOTE) {
+			donote(P->notes[P->noteout % NNOTE], 0);
+			ainc(&P->noteout);
+		}
+	}
+}
--- /dev/null
+++ b/arm.c
@@ -1,0 +1,640 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include <mach.h>
+#include "dat.h"
+#include "fns.h"
+
+enum {
+	fI = 1<<25,
+	fP = 1<<24,
+	fLi = 1<<24,
+	fU = 1<<23,
+	fB = 1<<22,
+	fW = 1<<21,
+	fL = 1<<20,
+	fS = 1<<20,
+	fSg = 1<<6,
+	fH = 1<<5,
+};
+
+static u32int
+arg(int n)
+{
+	/* no locking necessary, since we're on the stack */
+	return *(u32int*) vaddrnol(P->R[13] + 4 + 4 * n, 4, ARD);
+}
+
+static u32int
+asize(int n)
+{
+	/* keep in sync with Bhdr in pool.c */
+	u32int a;
+
+	a = *(u32int*) vaddrnol(P->R[13] + 4 + 4 * n, 4, ARD);
+	return *(ulong*) vaddrnol(a-4, 4, ARD);
+}
+
+void
+invalid(u32int instr)
+{
+	suicide("undefined instruction %8ux @ %8ux", instr, P->R[15] - 4);
+}
+
+u32int
+evenaddr(u32int addr, u32int mask)
+{
+	if((addr & mask) == 0)
+		return addr;
+	suicide("unaligned access %8ux @ %8ux", addr, P->R[15] - 4);
+	return addr & ~mask;
+}
+
+static u32int
+doshift(u32int instr, u8int *carry)
+{
+	ulong amount, val;
+
+	val = P->R[instr & 15];
+	if(instr & (1<<4)) {
+		if(instr & (1<<7))
+			invalid(instr);
+		amount = P->R[(instr >> 8) & 15] & 0xFF;
+		if(amount == 0)
+			return val;
+	} else {
+		amount = (instr >> 7) & 31;
+		if(amount == 0 && (instr & (3<<5)) != 0)
+			amount = 32;
+	}
+	switch((instr >> 5) & 3) {
+	default:
+		if(amount == 0)
+			return val;
+		if(amount < 32) {
+			*carry = (val >> (32 - amount)) & 1;
+			return val << amount;
+		}
+		*carry = val & 1;
+		return 0;
+	case 1:
+		if(amount < 32){
+			*carry = (val >> (amount - 1)) & 1;
+			return val >> amount;
+		}
+		*carry = val >> 31;
+		return 0;
+	case 2:
+		if(amount < 32){
+			*carry = (val >> (amount - 1)) & 1;
+			return ((long) val) >> amount;
+		}
+		if((long)val < 0){
+			*carry = 1;
+			return -1;
+		}
+		*carry = 0;
+		return 0;
+	case 3:
+		amount &= 31;
+		if(amount){
+			*carry = (val >> (amount - 1)) & 1;
+			return (val >> amount) | (val << (32 - amount));
+		}
+		amount = *carry & 1;
+		*carry = val & 1;
+		return (val>>1) | (amount<<31);
+	}
+}
+
+static void
+single(u32int instr)
+{
+	long offset;
+	u32int addr;
+	u32int *Rn, *Rd;
+	void *targ;
+	Segment *seg;
+	
+	if(instr & fI) {
+		u8int carry = 0;
+		if(instr & (1<<4))
+			invalid(instr);
+		offset = doshift(instr, &carry);
+	} else
+		offset = instr & ((1<<12) - 1);
+	if(!(instr & fU))
+		offset = - offset;
+	Rn = P->R + ((instr >> 16) & 15);
+	Rd = P->R + ((instr >> 12) & 15);
+	if((instr & (fW | fP)) == fW)
+		invalid(instr);
+	if(Rn == P->R + 15) {
+		if(instr & fW)
+			invalid(instr);
+		addr = P->R[15] + 4;
+	}else
+		addr = *Rn;
+	if(instr & fP)
+		addr += offset;
+	if((instr & fB) == 0)
+		addr = evenaddr(addr, 3);
+	switch(instr & (fB | fL)) {
+	case 0:
+		targ = vaddr(addr, 4, AWR, &seg);
+		*(u32int*) targ = *Rd;
+		break;
+	case fB:
+		targ = vaddr(addr, 1, AWR, &seg);
+		*(u8int*) targ = *Rd;
+		break;
+	case fL:
+		targ = vaddr(addr, 4, ARD, &seg);
+		*Rd = *(u32int*) targ;
+		break;
+	case fB | fL:
+		targ = vaddr(addr, 1, ARD, &seg);
+		*Rd = *(u8int*) targ;
+		break;
+	default:
+		targ = nil;
+		abort();
+	}
+	if(Rd == P->R + 15 && !(instr & fL)) {
+		if(instr & fB)
+			*(u8int*) targ += 8;
+		else
+			*(u32int*) targ += 8;
+	}
+	segunlock(seg);
+	if(!(instr & fP))
+		addr += offset;
+	if((instr & fW) || !(instr & fP))
+		*Rn = addr;
+}
+
+/* atomic compare and swap from libc */
+extern int cas(u32int *p, u32int old, u32int new);
+
+static void
+swap(u32int instr)
+{
+	u32int *Rm, *Rn, *Rd, *targ, addr, old, new;
+	Segment *seg;
+	
+	Rm = P->R + (instr & 15);
+	Rd = P->R + ((instr >> 12) & 15);
+	Rn = P->R + ((instr >> 16) & 15);
+	if(Rm == P->R + 15 || Rd == P->R + 15 || Rn == P->R + 15)
+		invalid(instr);
+	addr = *Rn;
+	if((instr & fB) == 0)
+		addr = evenaddr(addr, 3);
+	targ = (u32int *) vaddr(addr & ~3, 4, ARD|AWR, &seg);
+	do {
+		old = *targ;
+		new = *Rm;
+		if(instr & fB){
+			new &= 0xFF;
+			new <<= 8*(addr&3);
+			new |= old & ~(0xFF << 8*(addr&3));
+		}
+	} while(!cas(targ, old, new));
+	if(instr & fB) {
+		old >>= 8*(addr&3);
+		old &= 0xFF;
+	}
+	*Rd = old;
+	segunlock(seg);
+}
+
+static u32int
+add(u32int a, u32int b, u8int type, u8int *carry, u8int *overflow)
+{
+	u32int res1;
+	u64int res2;
+
+	if(type) {
+		res2 = (u64int)a - b + *carry - 1;
+		res1 = res2;
+		if(((a ^ b) & (1<<31)) && !((b ^ res1) & (1<<31)))
+			*overflow = 1;
+		else
+			*overflow = 0;
+		if(res2 & 0x100000000LL)
+			*carry = 0;
+		else
+			*carry = 1;	
+	} else {
+		res2 = (u64int)a + b + *carry;
+		res1 = res2;
+		if(!((a ^ b) & (1<<31)) && ((b ^ res1) & (1<<31)))
+			*overflow = 1;
+		else
+			*overflow = 0;
+		if(res2 & 0x100000000LL)
+			*carry = 1;
+		else *carry = 0;
+	}
+	return res1;
+}
+
+static void
+alu(u32int instr)
+{
+	u32int Rn, *Rd, operand, shift, result, op;
+	u8int carry, overflow;
+	
+	Rn = P->R[(instr >> 16) & 15];
+	Rd = P->R + ((instr >> 12) & 15);
+	if(((instr >> 16) & 15) == 15) {
+		Rn += 4;
+		if(!(instr & fI) && (instr & (1<<4)))
+			Rn += 4;
+	}
+	if(Rd == P->R + 15 && (instr & fS))
+		invalid(instr);
+
+	carry = (P->CPSR & flC) != 0;
+	overflow = (P->CPSR & flV) != 0;
+
+	if(instr & fI) {
+		operand = instr & 0xFF;
+		shift = ((instr >> 8) & 15) << 1;
+		if(shift){
+			operand = (operand >> shift) | (operand << (32 - shift));
+			carry = operand >> 31;
+		}
+	} else
+		operand = doshift(instr, &carry);
+
+	op = (instr >> 21) & 15;
+	if(op >= 8 && op <= 11 && !(instr & fS))
+		sysfatal("no PSR transfers plz");
+	if(op >= 5 && op < 8)
+		carry = (P->CPSR & flC) != 0;
+	switch(op) {
+	case 0: case 8: result = Rn & operand; break;
+	case 1: case 9: result = Rn ^ operand; break;
+	case 2: case 10: carry = 1; case 6: result = add(Rn, operand, 1, &carry, &overflow); break;
+	case 3:          carry = 1; case 7: result = add(operand, Rn, 1, &carry, &overflow); break;
+	case 4: case 11: carry = 0; case 5: result = add(operand, Rn, 0, &carry, &overflow); break;
+	case 12: result = Rn | operand; break;
+	case 13: result = operand; break;
+	case 14: result = Rn & ~operand; break;
+	case 15: result = ~operand; break;
+	default: result = 0; /* never happens */
+	}
+	if(instr & fS) {
+		P->CPSR &= ~FLAGS;
+		if(result == 0)
+			P->CPSR |= flZ;
+		if(result & (1<<31))
+			P->CPSR |= flN;
+		if(carry)
+			P->CPSR |= flC;
+		if(overflow)
+			P->CPSR |= flV;
+	}
+	if(op < 8 || op >= 12)
+		*Rd = result;
+}
+
+static void
+branch(u32int instr)
+{
+	long offset;
+	u32int npc;
+	
+	offset = instr & ((1<<24) - 1);
+	if(offset & (1<<23))
+		offset |= ~((1 << 24) - 1);
+	offset *= 4;
+	npc = P->R[15] + offset + 4;
+	if(instr & fLi){
+		P->R[14] = P->R[15];
+		if(check){
+			if(npc == mallocaddr){
+				P->hookarg[0] = arg(1);	/* size */
+				P->hookfn = hookmalloc;
+				P->hookpc = P->R[15];
+			}else if(npc == reallocaddr){
+				P->hookarg[0] = arg(1);	/* old */
+				P->hookarg[1] = asize(1);	/* oldsz */
+				P->hookarg[2] = arg(2);	/* size */
+				P->hookfn = hookrealloc;
+				P->hookpc = P->R[15];
+			}else if(npc == freeaddr){
+				P->hookarg[0] = arg(1);
+				P->hookarg[1] = asize(1);
+				P->hookfn = hookfree;
+				P->hookpc = P->R[15];
+			}
+		}
+	}
+	P->R[15] = npc;
+}
+
+static void
+halfword(u32int instr)
+{
+	u32int offset, target, *Rn, *Rd;
+	Segment *seg;
+	
+	if(instr & (1<<22)) {
+		offset = (instr & 15) | ((instr >> 4) & 0xF0);
+	} else {
+		if((instr & 15) == 15)
+			invalid(instr);
+		offset = P->R[instr & 15];
+	}
+	if(!(instr & fU))
+		offset = - offset;
+	if(!(instr & fP) && (instr & fW))
+		invalid(instr);
+	Rn = P->R + ((instr >> 16) & 15);
+	Rd = P->R + ((instr >> 12) & 15);
+	if(Rn == P->R + 15 || Rd == P->R + 15)
+		sysfatal("R15 in halfword");
+	target = *Rn;
+	if(instr & fP)
+		target += offset;
+	if(instr & fH)
+		target = evenaddr(target, 1);
+	switch(instr & (fSg | fH | fL)) {
+	case fSg:
+		*(u8int*) vaddr(target, 1, AWR, &seg) = *Rd;
+		break;
+	case fSg | fL:
+		*Rd = (long) *(char*) vaddr(target, 1, ARD, &seg);
+		break;
+	case fH: case fSg | fH:
+		*(u16int*) vaddr(target, 2, AWR, &seg) = *Rd;
+		break;
+	case fH | fL:
+		*Rd = *(u16int*) vaddr(target, 2, ARD, &seg);
+		break;
+	case fH | fL | fSg:
+		*Rd = (long) *(short*) vaddr(target, 2, ARD, &seg);
+		break;
+	}
+	segunlock(seg);
+	if(!(instr & fP))
+		target += offset;
+	if(!(instr & fP) || (instr & fW))
+		*Rn = target;
+}
+
+static void
+block(u32int instr)
+{
+	int i;
+	u32int targ, *Rn;
+	Segment *seg;
+
+	if(instr & (1<<22))
+		invalid(instr);
+	Rn = P->R + ((instr >> 16) & 15);
+	if(Rn == P->R + 15 || instr & (1<<15))
+		sysfatal("R15 block");
+	targ = evenaddr(*Rn, 3);
+	if(instr & fU) {
+		for(i = 0; i < 16; i++) {
+			if(!(instr & (1<<i)))
+				continue;
+			if(instr & fP)
+				targ += 4;
+			if(instr & fL)
+				P->R[i] = *(u32int*) vaddr(targ, 4, ARD, &seg);
+			else
+				*(u32int*) vaddr(targ, 4, AWR, &seg) = P->R[i];
+			segunlock(seg);
+			if(!(instr & fP))
+				targ += 4;
+		}
+	} else {
+		for(i = 15; i >= 0; i--) {
+			if(!(instr & (1<<i)))
+				continue;
+			if(instr & fP)
+				targ -= 4;
+			if(instr & fL)
+				P->R[i] = *(u32int*) vaddr(targ, 4, ARD, &seg);
+			else
+				*(u32int*) vaddr(targ, 4, AWR, &seg) = P->R[i];
+			segunlock(seg);
+			if(!(instr & fP))
+				targ -= 4;
+		}
+	}
+	if(instr & fW)
+		*Rn = targ;
+}
+
+static void
+multiply(u32int instr)
+{
+	u32int *Rd, *Rn, *Rs, *Rm, res;
+	
+	Rm = P->R + (instr & 15);
+	Rs = P->R + ((instr >> 8) & 15);
+	Rn = P->R + ((instr >> 12) & 15);
+	Rd = P->R + ((instr >> 16) & 15);
+	if(Rd == Rm || Rm == P->R + 15 || Rs == P->R + 15 || Rn == P->R + 15 || Rd == P->R + 15)
+		invalid(instr);
+	res = *Rm * *Rs;
+	if(instr & (1<<21))
+		res += *Rn;
+	*Rd = res;
+	if(instr & (1<<20)) {
+		P->CPSR &= ~(flN | flZ);
+		if(res & (1<<31))
+			P->CPSR |= flN;
+		if(res == 0)
+			P->CPSR |= flZ;
+	}
+}
+
+static void
+multiplylong(u32int instr)
+{
+	u32int *RdH, *RdL, *Rs, *Rm;
+	u64int res;
+	
+	Rm = P->R + (instr & 15);
+	Rs = P->R + ((instr >> 8) & 15);
+	RdL = P->R + ((instr >> 12) & 15);
+	RdH = P->R + ((instr >> 16) & 15);
+	if(RdL == RdH || RdH == Rm || RdL == Rm || Rm == P->R + 15
+	|| Rs == P->R + 15 || RdL == P->R + 15 || RdH == P->R + 15)
+		invalid(instr);
+	if(instr & (1<<22))
+		res = ((vlong)*(int*)Rs) * *(int*)Rm;
+	else {
+		res = *Rs;
+		res *= *Rm;
+	}
+	if(instr & (1<<21)) {
+		res += *RdL;
+		res += ((uvlong)*RdH) << 32;
+	}
+	*RdL = res;
+	*RdH = res >> 32;
+	if(instr & (1<<20)) {
+		P->CPSR &= ~FLAGS;
+		if(res == 0)
+			P->CPSR |= flN;
+		if(res & (1LL<<63))
+			P->CPSR |= flV;
+	}
+}
+
+static void
+singleex(u32int instr)
+{
+	u32int *Rn, *Rd, *Rm, *targ, addr;
+	Segment *seg;
+	
+	Rd = P->R + ((instr >> 12) & 15);
+	Rn = P->R + ((instr >> 16) & 15);
+	if(Rd == P->R + 15 || Rn == P->R + 15)
+		invalid(instr);
+	addr = evenaddr(*Rn, 3);
+	if(instr & fS) {
+		targ = vaddr(addr, 4, ARD, &seg);
+		*Rd = *targ;
+		P->lladdr = addr;
+		P->llval = *Rd;
+		segunlock(seg);
+	} else {
+		Rm = P->R + (instr & 15);
+		if(Rm == P->R + 15)
+			invalid(instr);
+		targ = vaddr(addr, 4, ARD|AWR, &seg);
+
+		/*
+		 * this is not quite correct as we will succeed even
+		 * if the value was modified and then restored to its
+		 * original value but good enougth approximation for
+		 * libc's _tas(), _cas() and _xinc()/_xdec().
+		 */
+		*Rd = addr != P->lladdr || !cas(targ, P->llval, *Rm);
+		segunlock(seg);
+		clrex();
+	}
+}
+
+void
+clrex(void)
+{
+	P->lladdr = 0;
+	P->llval = 0;
+}
+
+static void
+barrier(void)
+{
+	static Lock l;
+
+	lock(&l);
+	unlock(&l);
+}
+
+void
+step(void)
+{
+	u32int instr;
+	Segment *seg;
+
+	instr = *(u32int*) vaddr(P->R[15], 4, 0, &seg);
+	segunlock(seg);
+	if(fulltrace) {
+		print("%d ", P->pid);
+		if(havesymbols) {
+			Symbol s;
+			char buf[512];
+			
+			if(findsym(P->R[15], CTEXT, &s) >= 0)
+				print("%s ", s.name);
+			if(fileline(buf, 512, P->R[15]) >= 0)
+				print("%s ", buf);
+		}
+		print("%.8ux %.8ux %c%c%c%c\n", P->R[15], instr,
+			(P->CPSR & flZ) ? 'Z' : ' ',
+			(P->CPSR & flC) ? 'C' : ' ',
+			(P->CPSR & flN) ? 'N' : ' ',
+			(P->CPSR & flV) ? 'V' : ' '
+			);
+	}
+print("hookpc=%#ux\n", P->hookpc);
+	if(P->R[15] == P->hookpc){
+		P->hookfn(P->hookarg);
+		P->hookpc = 0;
+		P->hookfn = nil;
+	}
+	P->R[15] += 4;
+	switch(instr >> 28) {
+	case 0x0: if(!(P->CPSR & flZ)) return; break;
+	case 0x1: if(P->CPSR & flZ) return; break;
+	case 0x2: if(!(P->CPSR & flC)) return; break;
+	case 0x3: if(P->CPSR & flC) return; break;
+	case 0x4: if(!(P->CPSR & flN)) return; break;
+	case 0x5: if(P->CPSR & flN) return; break;
+	case 0x6: if(!(P->CPSR & flV)) return; break;
+	case 0x7: if(P->CPSR & flV) return; break;
+	case 0x8: if(!(P->CPSR & flC) || (P->CPSR & flZ)) return; break;
+	case 0x9: if((P->CPSR & flC) && !(P->CPSR & flZ)) return; break;
+	case 0xA: if(!(P->CPSR & flN) != !(P->CPSR & flV)) return; break;
+	case 0xB: if(!(P->CPSR & flN) == !(P->CPSR & flV)) return; break;
+	case 0xC: if((P->CPSR & flZ) || !(P->CPSR & flN) != !(P->CPSR & flV)) return; break;
+	case 0xD: if(!(P->CPSR & flZ) && !(P->CPSR & flN) == !(P->CPSR & flV)) return; break;
+	case 0xE: break;
+	case 0xF:
+		switch(instr & 0xFFF000F0){
+		case 0xF5700010:	/* CLREX */
+			clrex();
+			return;
+		case 0xF5700040:	/* DSB */
+		case 0xF5700050:	/* DMB */
+		case 0xF5700060:	/* ISB */
+			barrier();
+			return;
+		}
+	default: sysfatal("condition code %x not implemented (instr %ux, ps %ux)", instr >> 28, instr, P->R[15]);
+	}
+	if((instr & 0x0FB00FF0) == 0x01000090)
+		swap(instr);
+	else if((instr & 0x0FE000F0) == 0x01800090)
+		singleex(instr);
+	else if((instr & 0x0FC000F0) == 0x90)
+		multiply(instr);
+	else if((instr & 0x0F8000F0) == 0x800090)
+		multiplylong(instr);
+	else if((instr & ((1<<26) | (1<<27))) == (1 << 26))
+		single(instr);
+	else if((instr & 0x0E000090) == 0x90 && (instr & 0x60))
+		halfword(instr);
+	else if((instr & ((1<<26) | (1<<27))) == 0)
+		alu(instr);
+	else if((instr & (7<<25)) == (5 << 25))
+		branch(instr);
+	else if((instr & (15<<24)) == (15 << 24))
+		syscall();
+	else if((instr & (7<<25)) == (4 << 25))
+		block(instr);
+	else if((instr & 0x0E000F00) == 0x0C000100)
+		fpatransfer(instr);
+	else if((instr & 0x0E000F10) == 0x0E000100)
+		fpaoperation(instr);
+	else if((instr & 0x0E000F10) == 0x0E000110)
+		fparegtransfer(instr);
+	else if(vfp && ((instr & 0x0F000A10) == 0x0E000A00))
+		vfpoperation(instr);
+	else if(vfp && ((instr & 0x0F000F10) == 0x0E000A10))
+		vfpregtransfer(instr);
+	else if(vfp && ((instr & 0x0F000A00) == 0x0D000A00))
+		vfprmtransfer(instr);
+	else
+		invalid(instr);
+}
--- /dev/null
+++ b/chk.c
@@ -1,0 +1,105 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include <mach.h>
+#include "dat.h"
+#include "fns.h"
+
+static u32int
+arg(int n)
+{
+	/* no locking necessary, since we're on the stack */
+	return *(u32int*) vaddrnol(P->R[13] + 4 + 4 * n, 4, ARD);
+}
+
+void
+validmem(Segment *s, u32int off, u32int len)
+{
+	u32int end;
+
+	/*
+	 * when we're within a malloc or free operation, we're looking at
+	 * memory that we don't want to allow user programs to touch; skip
+	 * the check here.
+	 */
+	if(P->hookpc != 0)
+		return;
+	for(end = off+len; off != end; off++){
+		if((s->shadow[off>>3] & 1<<(off&3)) == 0){
+			print("invalid read of %#x at %#x\n", off, P->R[15] - 4);
+			abort();
+		}
+	}
+}
+
+void
+dumpmap(Segment *seg)
+{
+	int i;
+
+	for(i = 0; i < (seg->size + 7)/8; i++){
+		if(i % 40 == 0)
+			print("\n[%04x] ", seg->start+i*8);
+		print("%02ux", seg->shadow[i]);
+	}
+	print("\n");
+}
+
+void
+markvalid(Segment *s, u32int off, u32int len)
+{
+	u32int end;
+
+	for(end = off+len; off != end; off++)
+		s->shadow[off>>3] |= (1<<(off&3));
+}
+
+void
+markinvalid(Segment *s, u32int off, u32int len)
+{
+	u32int end;
+
+	for(end = off+len; off != end; off++)
+		s->shadow[off>>3] &= ~(1<<(off&3));
+}
+
+void
+hookmalloc(u32int *av)
+{
+	Segment *seg;
+	uchar *p;
+
+	print("malloced %#x+%d\n", P->R[0], av[0]);
+	p = vaddr(P->R[0], 0, 0, &seg);
+	dumpmap(seg);
+	markinvalid(seg, p - (uchar*)seg->data, av[0]);
+	dumpmap(seg);
+}
+
+
+void
+hookrealloc(u32int *av)
+{
+	Segment *seg;
+	uchar *p;
+
+	print("malloced %#x+%d => %#x+%d\n", av[0], av[1], P->R[0], av[2]);
+	p = vaddr(av[0], 0, 0, &seg);
+	markinvalid(seg, p - (uchar*)seg->data, av[1]);
+	p = vaddr(P->R[0], 0, 0, &seg);
+	markinvalid(seg, p - (uchar*)seg->data, av[2]);
+}
+
+void
+hookfree(u32int *av)
+{
+	Segment *seg;
+	uchar *p;
+
+	print("freed %#x+%d\n", av[0], av[1]);
+	p = vaddr(av[0], 0, 0, &seg);
+	dumpmap(seg);
+	markinvalid(seg, p - (uchar*)seg->data, av[1]);
+	dumpmap(seg);
+}
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,99 @@
+typedef struct Process Process;
+typedef struct Segment Segment;
+typedef struct Fdtable Fdtable;
+typedef struct Fd Fd;
+
+enum {
+	STACKSIZE = 0x100000,
+	NAMEMAX = 27,
+	NNOTE = 5,
+	SEGNUM = 8,
+	Nfpregs = 16,
+
+	flN = 1<<31,
+	flZ = 1<<30,
+	flC = 1<<29,
+	flV = 1<<28,
+	FLAGS = flN | flZ | flC | flV,
+};
+
+enum {
+	SEGTEXT,
+	SEGDATA,
+	SEGBSS,
+	SEGSTACK,
+};
+
+enum {
+	ARD	= 1<<0,
+	AWR	= 1<<1,
+};
+
+struct Process {
+	Process *prev, *next;	/* linked list (for fs) */
+	int pid;
+	char name[NAMEMAX+1];	/* name for status file */
+	Ref *path;		/* Ref + string data */
+
+	Segment *S[SEGNUM];	/* memory */
+
+	u32int lladdr;		/* LL/SC emulation */
+	u32int llval;
+
+	u32int R[16];		/* general purpose registers / PC (R15) */
+	u32int CPSR;		/* status register */
+
+	u32int FPSR;
+	long double F[Nfpregs];
+
+	char errbuf[ERRMAX];
+	Fd *fd;			/* bitmap of OCEXEC files */
+	
+	/* note handling */
+	u32int notehandler;
+	int innote;
+	jmp_buf notejmp;
+	char notes[ERRMAX][NNOTE];
+	long notein, noteout;
+
+	u32int	hookpc;
+	u32int	hookarg[3];
+	void	(*hookfn)(u32int*);
+};
+
+int vfp;
+
+extern void **_privates;
+extern int _nprivates;
+#define P (*(Process**)_privates)
+extern Ref nproc;
+extern Process plist;
+extern Lock plistlock;
+extern int check;
+u32int mallocaddr, freeaddr, reallocaddr;
+
+enum {
+	SEGFLLOCK = 1,
+};
+
+struct Segment {
+	Ref;
+	int flags;
+	RWLock rw; /* lock for SEGFLLOCK segments */
+	u32int start, size;
+	void *data;
+	uchar *shadow;
+	Ref *dref;
+};
+
+struct Fd {
+	RWLock;
+	Ref;
+	u8int *fds;
+	int nfds;
+};
+
+#define fulltrace 1
+#define havesymbols 1
+#define ultraverbose 0
+#define systrace 0
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,51 @@
+void *emalloc(u32int);
+void *emallocz(u32int);
+void *erealloc(void *, u32int);
+void initproc(void);
+int loadtext(char *, int, char **);
+Segment *newseg(u32int, u32int, int);
+void *vaddr(u32int, u32int, int, Segment **);
+void *vaddrnol(u32int, u32int, int);
+void step(void);
+void syscall(void);
+void cherrstr(char *, ...);
+u32int noteerr(u32int, u32int);
+void freesegs(void);
+Fd *newfd(void);
+Fd *copyfd(Fd *);
+void fddecref(Fd *);
+int iscexec(Fd *, int);
+void setcexec(Fd *, int, int);
+void cleanup(void);
+void clrex(void);
+void segunlock(Segment *);
+void *copyifnec(u32int, int, int *);
+void *bufifnec(u32int, int, int *);
+void copyback(u32int, int, void *);
+void initfs(char *, char *);
+void suicide(char *, ...);
+void fdclear(Fd *);
+void addproc(Process *);
+void remproc(Process *);
+Process *findproc(int);
+void donote(char *, ulong);
+void addnote(char *);
+void dump(void);
+void resetfpa(void);
+void invalid(u32int);
+u32int evenaddr(u32int,u32int);
+void fpatransfer(u32int);
+void fpaoperation(u32int);
+void fparegtransfer(u32int);
+void resetvfp(void);
+void vfpregtransfer(u32int);
+void vfprmtransfer(u32int);
+void vfpoperation(u32int);
+void inittos(void);
+void initmem(void);
+void validmem(Segment*, u32int, u32int);
+void markvalid(Segment*, u32int, u32int);
+void markinvalid(Segment*, u32int, u32int);
+void hookmalloc(u32int*);
+void hookrealloc(u32int*);
+void hookfree(u32int*);
--- /dev/null
+++ b/fpa.c
@@ -1,0 +1,149 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include <mach.h>
+#include "dat.h"
+#include "fns.h"
+
+void
+resetfpa(void)
+{
+	int i;
+	
+	P->FPSR = 0x81000000;
+	for(i = 0; i < 8; i++)
+		P->F[i] = 0;
+}
+
+void
+fpatransfer(u32int instr)
+{
+	enum {
+		fP = 1<<24,
+		fU = 1<<23,
+		fT1 = 1<<22,
+		fW = 1<<21,
+		fL = 1<<20,
+		fT0 = 1<<15,
+	};
+
+	long double *Fd;
+	u32int *Rn, addr;
+	int off;
+	void *targ;
+	Segment *seg;
+
+	Rn = P->R + ((instr >> 16) & 15);
+	Fd = P->F + ((instr >> 12) & 7);
+	if(Rn == P->R + 15)
+		invalid(instr);
+	off = (instr & 255) * 4;
+	if(!(instr  & fU))
+		off = -off;
+	addr = *Rn;
+	if(instr & fP)
+		addr += off;
+	targ = vaddr(addr, 8, 0, &seg);
+	switch(instr & (fT0 | fT1 | fL)) {
+	case 0:		*(float *) targ = *Fd; break;
+	case fL:	*Fd = *(float *) targ; break;
+	case fT0:	*(double *) targ = *Fd; break;
+	case fT0 | fL:	*Fd = *(double *) targ; break;
+	default:	invalid(instr);
+	}
+	segunlock(seg);
+	if(!(instr & fP))
+		addr += off;
+	if(instr & fW)
+		*Rn = addr;
+}
+
+static long double
+fpasecop(u32int instr)
+{
+	switch(instr & 15) {
+	case 8: return 0.0; break;
+	case 9: return 1.0; break;
+	case 10: return 2.0; break;
+	case 11: return 3.0; break;
+	case 12: return 4.0; break;
+	case 13: return 5.0; break;
+	case 14: return 0.5; break;
+	case 15: return 10.0; break;
+	}
+	return P->F[instr & 7];
+}
+
+void
+fpaoperation(u32int instr)
+{
+	long double *Fn, *Fd, op, op2, res;
+	int prec, opc;
+	
+	Fn = P->F + ((instr >> 16) & 7);
+	Fd = P->F + ((instr >> 12) & 7);
+	op2 = fpasecop(instr);
+	op = *Fn;
+	prec = ((instr >> 7) & 1) | ((instr >> 18) & 2);
+	opc = ((instr >> 20) & 15) | ((instr >> 11) & 16);
+	switch(opc) {
+	case 0: res = op + op2; break;
+	case 1: res = op * op2; break;
+	case 2: res = op - op2; break;
+	case 3: res = op2 - op; break;
+	case 4: res = op / op2; break;
+	case 5: res = op2 / op; break;
+	case 16: res = op2; break;
+	case 17: res = - op2; break;
+	case 18: res = fabs(op2); break;
+	case 19: res = (vlong) op2; break;
+	case 20: res = sqrt(op2); break;
+	default: sysfatal("unimplemented FPA operation %#x @ %8ux", opc, P->R[15] - 4);
+	return;
+	}
+	switch(prec) {
+	case 0: *Fd = (float) res; break;
+	case 1: *Fd = (double) res; break;
+	case 2: *Fd = res; break;
+	default: invalid(instr);
+	}
+}
+
+void
+fparegtransfer(u32int instr)
+{
+	u32int *Rd;
+	long tmp;
+	long double *Fn, op, op2;
+	
+	Rd = P->R + ((instr >> 12) & 15);
+	Fn = P->F + ((instr >> 16) & 7);
+	op = fpasecop(instr);
+	if(Rd == P->R + 15) {
+		op2 = *Fn;
+		switch((instr >> 21) & 7) {
+		case 4: break;
+		case 5: op = - op; break;
+		default: invalid(instr);
+		}
+		if(op2 < op)
+			P->CPSR = (P->CPSR & ~FLAGS) | flN;
+		else if(op2 >= op) {
+			P->CPSR = (P->CPSR & ~FLAGS) | flC;
+			if(op2 == op)
+				P->CPSR |= flZ;
+		} else
+			P->CPSR = (P->CPSR & ~FLAGS) | flV;
+		return;
+	}
+	if(instr & (1<<3))
+		invalid(instr);
+	switch((instr >> 20) & 15) {
+	case 0: *Fn = *(long *) Rd; break;
+	case 1: tmp = op; *Rd = tmp; break;
+	case 2: P->FPSR = *Rd; break;
+	case 3: *Rd = P->FPSR; break;
+	default: invalid(instr);
+	}
+}
--- /dev/null
+++ b/fs.c
@@ -1,0 +1,443 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+static char *luser;
+extern int pflag;
+
+enum
+{
+	Qdir,
+	Qtrace,
+	Qargs,
+	Qctl,
+	Qfd,
+	Qfpregs,
+	Qkregs,
+	Qmem,
+	Qnote,
+	Qnoteid,
+	Qnotepg,
+	Qns,
+	Qproc,
+	Qregs,
+	Qsegment,
+	Qstatus,
+	Qtext,
+	Qwait,
+	Qprofile,
+	Qsyscall,
+	NQid,
+};
+
+typedef struct Aux Aux;
+typedef struct Dirtab Dirtab;
+struct Dirtab {
+	char name[28];
+	Qid qid;
+	vlong length;
+	long perm;
+};
+struct Aux {
+	Process *p;
+	int fd;
+	Dirtab *d;
+};
+
+Dirtab procdir[] =
+{
+	"args",		{Qargs},	0,			0660,
+	"ctl",		{Qctl},		0,			0600,
+	"fd",		{Qfd},		0,			0444,
+	"fpregs",	{Qfpregs},	0,			0400,
+	"kregs",	{Qkregs},	18 * 4,			0400,
+	"mem",		{Qmem},		0,			0400,
+	"note",		{Qnote},	0,			0000,
+	"noteid",	{Qnoteid},	0,			0664,
+	"notepg",	{Qnotepg},	0,			0000,
+	"ns",		{Qns},		0,			0444,
+	"proc",		{Qproc},	0,			0400,
+	"regs",		{Qregs},	18 * 4,			0400,
+	"segment",	{Qsegment},	0,			0444,
+	"status",	{Qstatus},	176,			0444,
+	"text",		{Qtext},	0,			0400,
+	"wait",		{Qwait},	0,			0400,
+	"profile",	{Qprofile},	0,			0400,
+	"syscall",	{Qsyscall},	0,			0400,	
+	"",		{0},		0,			0,
+};
+
+static char *
+readin(int pid, char *file)
+{
+	char *name, *buf;
+	int fd, rc;
+	
+	name = smprint("/proc/%d/%s", pid, file);
+	fd = open(name, OREAD);
+	if(fd < 0){
+		free(name);
+		return nil;
+	}
+	buf = malloc(1024);
+	rc = read(fd, buf, 1023);
+	if(rc < 0){
+		free(buf);
+		free(name);
+		close(fd);
+		return nil;
+	}
+	buf[rc] = 0;
+	free(name);
+	close(fd);
+	return buf;
+}
+
+static int
+calcmem(Process *p)
+{
+	int i, r;
+	
+	r = 0;
+	for(i = 0; i < SEGNUM; i++) {
+		if(i == SEGSTACK)
+			continue;
+		if(p->S[i] == nil)
+			continue;
+		r += p->S[i]->size;
+	}
+	r = (r + 1023) / 1024;
+	return r;
+}
+
+static int
+copymem(Process *p, char *buf, u32int addr, int len)
+{
+	int i, n, r;
+
+	r = len;
+	while(len > 0) {
+		for(i = 0; i < SEGNUM; i++) {
+			if(p->S[i] == nil)
+				continue;
+			if(p->S[i]->start <= addr && p->S[i]->start + p->S[i]->size > addr)
+				break;
+		}
+		if(i == SEGNUM) {
+			werrstr("bad arg in syscall");
+			return -1;
+		}
+		n = p->S[i]->start + p->S[i]->size - addr;
+		if(n > len)
+			n = len;
+		memcpy(buf, (char*)p->S[i]->data + addr - p->S[i]->start, n);
+		len -= n;
+		buf += n;
+	}
+	return r;
+}
+
+static char *
+segments(Process *p)
+{
+	char *r, *s;
+	static char *names[] = {
+		[SEGTEXT] "Text",
+		[SEGSTACK] "Stack",
+		[SEGDATA] "Data",
+		[SEGBSS] "Bss",
+	};
+	int i;
+	
+	r = emalloc(1024);
+	s = r;
+	for(i = 0; i < SEGNUM; i++) {
+		if(p->S[i] == nil)
+			continue;
+		s += sprint(s, "%-7s%c  %.8ux %.8ux %4ld\n", names[i], i == SEGTEXT ? 'R' : ' ', p->S[i]->start, p->S[i]->start + p->S[i]->size, p->S[i]->dref->ref);
+	}
+	return r;
+}
+
+static void
+procattach(Req *req)
+{
+	req->fid->qid = (Qid) {0, 0, 0x80};
+	req->fid->aux = emallocz(sizeof(Aux));
+	((Aux *) req->fid->aux)->fd = -1;
+	req->ofcall.qid = req->fid->qid;
+	respond(req, nil);
+}
+
+static char *
+procwalk(Fid *fid, char *name, Qid *qid)
+{
+	int pid;
+	char buf[20];
+	Dirtab *d;
+	Aux *a;
+	
+	a = fid->aux;
+	if(fid->qid.path == 0) {
+		pid = atoi(name);
+		sprint(buf, "%d", pid);
+		if(strcmp(buf, name) != 0 || (a->p = findproc(pid)) == nil)
+			return "file does not exist";
+		*qid = (Qid) {pid * NQid, 0, 0x80};
+		fid->qid = *qid;
+		return nil;
+	}
+	if((fid->qid.path % NQid) == 0) {
+		for(d = procdir; d->name[0] != 0; d++)
+			if(strcmp(d->name, name) == 0)
+				break;
+		if(d->name[0] == 0)
+			return "file does not exist";
+		*qid = d->qid;
+		qid->path += fid->qid.path;
+		fid->qid = *qid;
+		a->d = d;
+		return nil;
+	}
+	return "the front fell off";
+}
+
+static char *
+procclone(Fid *old, Fid *new)
+{
+	new->aux = emallocz(sizeof(Aux));
+	memcpy(new->aux, old->aux, sizeof(Aux));
+	return nil;
+}
+
+static void
+procopen(Req *req)
+{
+	Aux *a;
+	
+	a = req->fid->aux;
+	switch((int)(req->fid->qid.path % NQid)) {
+	case Qtext:
+		a->fd = open((char*)(a->p->path + 1), OREAD);
+		break;
+	default:
+		respond(req, nil);
+		return;
+	}
+	if(a->fd < 0)
+		responderror(req);
+	else
+		respond(req, nil);
+}
+
+static void
+procdestroyfid(Fid *fid)
+{
+	Aux *a;
+	
+	a = fid->aux;
+	free(a);
+}
+
+static int
+procgen(int n, Dir *d, void *)
+{
+	int i;
+	Process *p;
+	
+	p = &plist;
+	for(i = 0;; i++) {
+		p = p->next;
+		if(p == &plist)
+			return -1;
+		if(i == n)
+			break;
+	}
+	d->uid = estrdup9p(luser);
+	d->gid = estrdup9p(luser);
+	d->muid = estrdup9p(luser);
+	d->name = smprint("%d", p->pid);
+	d->mode = DMDIR | 0555;
+	d->qid = (Qid) {p->pid * NQid, 0, 0x80};
+	return 0;
+}
+
+static int
+procsubgen(int n, Dir *d, void *)
+{
+	Dirtab *di;
+	
+	if(n >= nelem(procdir) - 1)
+		return -1;
+	
+	di = procdir + n;
+	d->uid = estrdup9p(luser);
+	d->gid = estrdup9p(luser);
+	d->muid = estrdup9p(luser);
+	d->name = estrdup9p(di->name);
+	d->mode = di->perm;
+	d->length = di->length;
+	d->qid = di->qid;
+	return 0;
+}
+
+static void
+procread(Req *req)
+{
+	Aux *a;
+	Process *p;
+	char *buf;
+	int rc;
+
+	a = req->fid->aux;
+	if(a == nil) {
+		respond(req, "the front fell off");
+		return;
+	}
+	if(req->fid->qid.path == 0) {
+		dirread9p(req, procgen, nil);
+		respond(req, nil);
+		return;
+	}
+	p = a->p;
+	switch((int)(req->fid->qid.path % NQid)) {
+	case Qdir:
+		dirread9p(req, procsubgen, nil);
+		respond(req, nil);
+		break;
+	case Qstatus:
+		buf = readin(p->pid, "status");
+		if(buf == nil)
+			responderror(req);
+		else {
+			memset(buf, ' ', 27);
+			memcpy(buf, p->name, strlen(p->name));
+			sprint(buf + 149, "%d", calcmem(p));
+			buf[strlen(buf)] = ' ';
+			readstr(req, buf);
+			free(buf);
+			respond(req, nil);
+		}
+		break;
+	case Qsegment:
+		buf = segments(p);
+		readstr(req, buf);
+		free(buf);
+		respond(req, nil);
+		break;
+	case Qtext:
+		rc = pread(a->fd, req->ofcall.data, req->ifcall.count, req->ifcall.offset);
+		if(rc >= 0) {
+			req->ofcall.count = rc;
+			respond(req, nil);
+		} else
+			responderror(req);
+		break;
+	case Qmem:
+		rc = copymem(p, req->ofcall.data, req->ifcall.offset, req->ifcall.count);
+		if(rc >= 0) {
+			req->ofcall.count = rc;
+			respond(req, nil);
+		} else
+			responderror(req);
+		break;
+	case Qregs:
+		buf = emallocz(18 * 4);
+		memcpy(buf, p->R, 15 * 4);
+		memcpy(buf + 16 * 4, &p->CPSR, 4);
+		memcpy(buf + 17 * 4, p->R + 15, 4);
+		readbuf(req, buf, 18 * 4);
+		free(buf);
+		respond(req, nil);
+		break;
+	default:
+		respond(req, "the front fell off");
+	}
+}
+
+static void
+writeto(Req *req, char *fmt, ...)
+{
+	int fd, rc;
+	va_list va;
+	char *file;
+	
+	va_start(va, fmt);
+	file = vsmprint(fmt, va);
+	va_end(va);
+	fd = open(file, OWRITE);
+	free(file);
+	if(fd < 0) {
+		responderror(req);
+		return;
+	}
+	rc = write(fd, req->ifcall.data, req->ifcall.count);
+	req->ofcall.count = rc;
+	if(rc < req->ifcall.count)
+		responderror(req);
+	else
+		respond(req, nil);
+	close(fd);
+}
+
+static void
+procwrite(Req *req)
+{
+	switch((int)(req->fid->qid.path % NQid)) {
+	case Qnote:
+		writeto(req, "/proc/%lld/note", req->fid->qid.path / NQid);
+		break;
+	default:
+		respond(req, "the front fell off");
+	}
+}
+
+static void
+procstat(Req *req)
+{
+	Aux *a;
+	Dir *d;
+	
+	d = &req->d;
+	a = req->fid->aux;
+	if(a == nil) {
+		respond(req, "the front fell off");
+		return;
+	}
+	d->qid = req->fid->qid;
+	if(a->d != nil) {
+		d->mode = a->d->perm;
+		d->length = a->d->length;
+		d->name = strdup(a->d->name);
+	} else {
+		d->mode = 0555 | DMDIR;
+		if(d->qid.path != 0)
+			d->name = smprint("%lld", d->qid.path / NQid);
+	}
+	d->uid = strdup(luser);
+	d->gid = strdup(luser);
+	d->muid = strdup(luser);
+	respond(req, nil);
+}
+
+static Srv procsrv = {
+	.attach = procattach,
+	.walk1 = procwalk,
+	.clone = procclone,
+	.destroyfid = procdestroyfid,
+	.open = procopen,
+	.read = procread,
+	.stat = procstat,
+};
+
+void
+initfs(char *name, char *mtpt)
+{
+	luser = getuser();
+	remove("/srv/armproc");
+	postmountsrv(&procsrv, name, mtpt, MREPL);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,8 @@
+</$objtype/mkfile
+
+TARG=5e
+OFILES=5e.$O seg.$O proc.$O util.$O arm.$O sys.$O fs.$O fpa.$O vfp.$O chk.$O
+HFILES=dat.h fns.h
+BIN=/$objtype/bin
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/proc.c
@@ -1,0 +1,446 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include <mach.h>
+#include <ctype.h>
+#include "dat.h"
+#include "fns.h"
+
+#pragma pack on
+typedef struct Tos Tos;
+struct Tos {
+	struct			/* Per process profiling */
+	{
+		ulong	pp;	/* known to be 0(ptr) */
+		ulong	next;	/* known to be 4(ptr) */
+		ulong	last;
+		ulong	first;
+		ulong	pid;
+		ulong	what;
+	} prof;
+	uvlong	cyclefreq;	/* cycle clock frequency if there is one, 0 otherwise */
+	vlong	kcycles;	/* cycles spent in kernel */
+	vlong	pcycles;	/* cycles spent in process (kernel + user) */
+	ulong	pid;		/* might as well put the pid here */
+	ulong	clock;
+	/* top of stack is here */
+};
+#pragma pack off
+
+Process plist;
+Lock plistlock;
+
+void
+initproc(void)
+{
+	P = emallocz(sizeof(Process));
+	P->pid = getpid();
+	P->fd = newfd();
+	incref(&nproc);
+	plist.prev = P;
+	plist.next = P;
+	P->prev = &plist;
+	P->next = &plist;
+	if(vfp)
+		resetvfp();
+	else
+		resetfpa();
+}
+
+void
+addproc(Process *p)
+{
+	lock(&plistlock);
+	p->prev = plist.prev;
+	p->next = &plist;
+	plist.prev->next = p;
+	plist.prev = p;
+	unlock(&plistlock);
+}
+
+void
+remproc(Process *p)
+{
+	lock(&plistlock);
+	p->prev->next = p->next;
+	p->next->prev = p->prev;
+	unlock(&plistlock);
+}
+
+Process *
+findproc(int pid)
+{
+	Process *p;
+
+	lock(&plistlock);
+	for(p = plist.next; p != &plist; p = p->next)
+		if(p->pid == pid)
+			break;
+	unlock(&plistlock);
+	if(p != &plist)
+		return p;
+	return nil;
+}
+
+static void
+copyname(char *file)
+{
+	char *p;
+	
+	p = strrchr(file, '/');
+	if(p == nil)
+		p = file;
+	else
+		p++;
+	strncpy(P->name, p, NAMEMAX);
+
+	if(P->path != nil && decref(P->path) == 0)
+		free(P->path);
+	P->path = emallocz(sizeof(Ref) + strlen(file)+1);
+	incref(P->path);
+	strcpy((char*)(P->path + 1), file);
+}
+
+static void
+initstack(int argc, char **argv)
+{
+	ulong tos, sp, ap, size, i, len;
+	
+	tos = (mach->utop & ~7) - sizeof(Tos) * 2;
+	sp = tos;
+	
+	size = 8;
+	for(i = 0; i < argc; i++)
+		size += strlen(argv[i]) + 5;
+	
+	sp -= size;
+	sp &= ~7;
+	P->R[0] = tos;
+	P->R[1] = mach->utop - 4;
+	P->R[13] = sp;
+	
+	*(ulong *) vaddrnol(sp, 4, AWR) = argc;
+	sp += 4;
+	ap = sp + (argc + 1) * 4;
+	for(i = 0; i < argc; i++) {
+		*(ulong *) vaddrnol(sp, 4, AWR) = ap;
+		sp += 4;
+		len = strlen(argv[i]) + 1;
+		memcpy(vaddrnol(ap, len, AWR), argv[i], len);
+		ap += len;
+	}
+	*(ulong *) vaddrnol(sp, 4, AWR) = 0;
+	inittos();
+}
+
+void
+inittos(void)
+{
+	ulong tos;
+
+	tos = (mach->utop & ~7) - sizeof(Tos) * 2;
+	((Tos *) vaddrnol(tos, sizeof(Tos), AWR))->pid = P->pid;
+}
+
+static int
+loadscript(int fd, char *file, int argc, char **argv)
+{
+	char buf[513], *p, **q, **nargv;
+	int rc, nargc, i;
+	
+	seek(fd, 0, 0);
+	rc = readn(fd, buf, 512);
+	if(rc <= 0)
+		goto invalid;
+	close(fd);
+	buf[rc] = 0;
+	p = strchr(buf, '\n');
+	if(p == nil)
+		goto invalid;
+	*p = 0;
+	while(isspace(*--p))
+		*p = 0;
+	nargc = 0;
+	p = buf + 2;
+	while(*p) {
+		while(*p && isspace(*p))
+			p++;
+		nargc++;
+		while(*p && !isspace(*p))
+			p++;
+	}
+	if(nargc == 0)
+		goto invalid;
+	nargv = emallocz(sizeof(char *) * (nargc + argc));
+	q = nargv;
+	p = buf + 2;
+	while(*p) {
+		while(*p && isspace(*p))
+			p++;
+		*(p-1) = 0;
+		*q++ = p;
+		while(*p && !isspace(*p))
+			p++;
+	}
+	*q++ = file;
+	for(i = 1; i < argc; i++)
+		*q++ = argv[i];
+	rc = loadtext(*nargv, argc + nargc, nargv);
+	free(nargv);
+	return rc;
+
+invalid:
+	werrstr("exec header invalid");
+	return -1;
+}
+
+static void
+inithooks(void)
+{
+	Symbol s;
+
+	if(lookup("poolalloc", nil, &s) == -1)
+		mallocaddr = -1;
+	else
+		mallocaddr = s.value;
+	if(lookup("poolrealloc", nil, &s) == -1)
+		reallocaddr = -1;
+	else
+		reallocaddr = s.value;
+	if(lookup("poolfree", nil, &s) == -1)
+		freeaddr = -1;
+	else
+		freeaddr = s.value;
+print("malloc: %#x, realloc: %#x, free %#x\n", mallocaddr, reallocaddr, freeaddr);
+}
+
+int
+loadtext(char *file, int argc, char **argv)
+{
+	int fd;
+	Fhdr fp;
+	Segment *text, *data, *bss;
+	char buf[2];
+	
+	fd = open(file, OREAD);
+	if(fd < 0) return -1;
+	if(pread(fd, buf, 2, 0) == 2 && buf[0] == '#' && buf[1] == '!')
+		return loadscript(fd, file, argc, argv);
+	seek(fd, 0, 0);
+	if(crackhdr(fd, &fp) == 0 || fp.magic != E_MAGIC) {
+		werrstr("exec header invalid");
+		return -1;
+	}
+	copyname(file);
+	P->notehandler = P->innote = P->notein = P->noteout = 0;
+	freesegs();
+	memset(P->R, 0, sizeof(P->R));
+	P->CPSR = 0;
+	text = newseg(fp.txtaddr - fp.hdrsz, fp.txtsz + fp.hdrsz, SEGTEXT);
+	data = newseg(fp.dataddr, fp.datsz, SEGDATA);
+	bss = newseg(fp.dataddr + fp.datsz, fp.bsssz, SEGBSS);
+	if(check){
+		markvalid(text, 0, fp.txtsz+fp.hdrsz);
+		markvalid(data, 0, fp.datsz);
+		markvalid(bss, 0, fp.bsssz);
+	}
+	newseg((mach->utop & ~7) - STACKSIZE, STACKSIZE, SEGSTACK);
+	seek(fd, fp.txtoff - fp.hdrsz, 0);
+	if(readn(fd, text->data, fp.txtsz + fp.hdrsz) < fp.txtsz + fp.hdrsz)
+		sysfatal("%r");
+	seek(fd, fp.datoff, 0);
+	if(readn(fd, data->data, fp.datsz) < fp.datsz)
+		sysfatal("%r");
+	memset(bss->data, 0, bss->size);
+	P->R[15] = fp.entry;
+	if(havesymbols || check)
+	if(syminit(fd, &fp) < 0)
+		fprint(2, "initializing symbol table: %r\n");
+	if(check)
+		inithooks();
+	close(fd);
+	fdclear(P->fd);
+	initstack(argc, argv);
+	if(vfp)
+		resetvfp();
+	else
+		resetfpa();
+	return 0;
+}
+
+void
+cherrstr(char *str, ...)
+{
+	va_list va;
+	
+	va_start(va, str);
+	vsnprint(P->errbuf, ERRMAX, str, va);
+	va_end(va);
+}
+
+u32int
+noteerr(u32int x, u32int y)
+{
+	if(((int)x) >= ((int)y))
+		return x;
+	rerrstr(P->errbuf, ERRMAX);
+	return x;
+}
+
+Fd *
+newfd(void)
+{
+	Fd *fd;
+	
+	fd = emallocz(sizeof(*fd));
+	incref(fd);
+	return fd;
+}
+
+Fd *
+copyfd(Fd *old)
+{
+	Fd *new;
+	
+	rlock(old);
+	new = newfd();
+	if(old->nfds > 0) {
+		new->nfds = old->nfds;
+		new->fds = emalloc(old->nfds);
+		memcpy(new->fds, old->fds, old->nfds);
+	}
+	runlock(old);
+	return new;
+}
+
+void
+fddecref(Fd *fd)
+{
+	if(decref(fd) == 0) {
+		free(fd->fds);
+		free(fd);
+	}
+}
+
+int
+iscexec(Fd *fd, int n)
+{
+	int r;
+
+	r = 0;
+	rlock(fd);
+	if(n / 8 < fd->nfds)
+		r = (fd->fds[n / 8] & (1 << (n % 8))) != 0;
+	runlock(fd);
+	return r;
+}
+
+void
+setcexec(Fd *fd, int n, int status)
+{
+	int old;
+
+	wlock(fd);
+	if(n / 8 >= fd->nfds) {
+		if(status == 0) {
+			wunlock(fd);
+			return;
+		}
+		old = fd->nfds;
+		fd->nfds = (n / 8) + 1;
+		fd->fds = erealloc(fd->fds, fd->nfds);
+		memset(fd->fds + old, 0, fd->nfds - old);
+	}
+	if(status == 0)
+		fd->fds[n / 8] &= ~(1 << (n % 8));
+	else
+		fd->fds[n / 8] |= (1 << (n % 8));
+	wunlock(fd);
+}
+
+void
+fdclear(Fd *fd)
+{
+	int i, j, k;
+
+	wlock(fd);
+	if(fd->nfds == 0) {
+		wunlock(fd);
+		return;
+	}
+	for(i = 0; i < fd->nfds; i++) {
+		j = fd->fds[i];
+		for(k = 0; k < 8; k++)
+			if(j & (1<<k))
+				close(8 * i + k);
+	}
+	free(fd->fds);
+	fd->nfds = 0;
+	fd->fds = nil;
+	wunlock(fd);
+}
+
+/* call this from a notehandler if you don't want the front to fall off */
+void
+addnote(char *msg)
+{
+	int new;
+	
+	new = P->notein + 1;
+	if((new - P->noteout) % NNOTE == 0)
+		return;
+
+	strncpy(P->notes[P->notein % NNOTE], msg, ERRMAX - 1);
+	P->notein = new;
+}
+
+/* the following code is not for the weak of heart */
+void
+donote(char *msg, ulong type)
+{
+	int rc;
+	u32int *ureg, *sp, uregp, msgp;
+	char *msgb;
+
+	if(P->notehandler == 0)
+		exits(msg);
+
+	clrex();
+	uregp = P->R[13] - 18 * 4;
+	ureg = vaddrnol(uregp, 18 * 4, 0);
+	memcpy(ureg, P->R, 15 * 4);
+	ureg[15] = type;
+	ureg[16] = P->CPSR;
+	ureg[17] = P->R[15];
+	P->R[13] = uregp;
+	msgp = P->R[13] -= ERRMAX;
+	msgb = vaddrnol(msgp, ERRMAX, 0);
+	strncpy(msgb, msg, ERRMAX);
+	P->R[13] -= 3 * 4;
+	sp = vaddrnol(P->R[13], 3 * 4, 0);
+	sp[0] = 0;
+	sp[2] = msgp;
+	P->R[0] = uregp;
+	P->R[15] = P->notehandler;
+	P->innote = 1;
+	switch(rc = setjmp(P->notejmp) - 1) {
+	case -1:
+		for(;;) {
+			if(ultraverbose)
+				dump();
+			step();
+		}
+	case NDFLT:
+		exits(msg);
+	case NCONT:
+		break;
+	default:
+		sysfatal("unhandled noted argument %d", rc);
+	}
+	P->innote = 0;
+	ureg = vaddrnol(uregp, 18 * 4, 0); /* just to be sure */
+	memcpy(P->R, ureg, 15 * 4);
+	P->CPSR = ureg[16];
+	P->R[15] = ureg[17];
+}
--- /dev/null
+++ b/seg.c
@@ -1,0 +1,144 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+Segment *
+newseg(u32int start, u32int size, int idx)
+{
+	Segment *s;
+	
+	s = emallocz(sizeof *s);
+	incref(s);
+	s->start = start;
+	s->size = size;
+	s->dref = emalloc(size + sizeof(Ref));
+	memset(s->dref, 0, sizeof(Ref));
+	incref(s->dref);
+	s->data = s->dref + 1;
+	if(check)
+		s->shadow = emalloc((size+7)/8);
+	if(idx == SEGBSS)
+		s->flags = SEGFLLOCK;
+	P->S[idx] = s;
+	return s;
+}
+
+void
+freesegs(void)
+{
+	Segment **s;
+	
+	for(s = P->S; s < P->S + SEGNUM; s++) {
+		if(*s == nil)
+			continue;
+		if(decref((*s)->dref) == 0)
+			free((*s)->dref);
+		if(decref(*s) == 0)
+			free(*s);
+		*s = nil;
+	}
+}
+
+void *
+vaddr(u32int addr, u32int len, int op, Segment **seg)
+{
+	Segment **ss, *s;
+	u32int off;
+
+	for(ss = P->S; ss < P->S + SEGNUM; ss++) {
+		if(*ss == nil)
+			continue;
+		s = *ss;
+		if(addr >= s->start && addr < s->start + s->size) {
+			if(addr + len > s->start + s->size)
+				break;
+			if(s->flags & SEGFLLOCK)
+				rlock(&s->rw);
+			*seg = s;
+			off = (addr - s->start);
+			if(check){
+				if(op&ARD) validmem(s, off, len);
+				if(op&AWR) markvalid(s, off, len);
+			}
+			return (char *)s->data + off;
+		}
+	}
+	suicide("fault %.8ux (%d) @ %.8ux", addr, len, P->R[15]);
+	return nil;
+}
+
+void *
+vaddrnol(u32int addr, u32int len, int op)
+{
+	Segment *seg;
+	void *ret;
+	
+	ret = vaddr(addr, len, op, &seg);
+	segunlock(seg);
+	return ret;
+}
+
+/* might be made a macro for hurr durr performance */
+void
+segunlock(Segment *s)
+{
+	if(s->flags & SEGFLLOCK)
+		runlock(&s->rw);
+}
+
+void *
+copyifnec(u32int addr, int len, int *copied)
+{
+	void *targ, *ret;
+	Segment *seg;
+	
+	targ = vaddr(addr, len > 0 ? len : 0, 0, &seg);
+	if((seg->flags & SEGFLLOCK) == 0) {
+		*copied = 0;
+		return targ;
+	}
+	if(len < 0)
+		len = strlen(targ) + 1;
+	ret = emalloc(len);
+	setmalloctag(ret, getcallerpc(&addr));
+	memcpy(ret, targ, len);
+	segunlock(seg);
+	*copied = 1;
+	return ret;
+}
+
+void *
+bufifnec(u32int addr, int len, int *buffered)
+{
+	void *targ, *v;
+	Segment *seg;
+	
+	targ = vaddr(addr, len, 0, &seg);
+	if((seg->flags & SEGFLLOCK) == 0) {
+		*buffered = 0;
+		return targ;
+	}
+	segunlock(seg);
+	*buffered = 1;
+	v = emalloc(len);
+	setmalloctag(v, getcallerpc(&addr));
+	return v;
+}
+
+void
+copyback(u32int addr, int len, void *data)
+{
+	void *targ;
+	Segment *seg;
+
+	if(len <= 0) {
+		free(data);
+		return;
+	}
+	targ = vaddr(addr, len, 0, &seg);
+	memmove(targ, data, len);
+	segunlock(seg);
+	free(data);
+}
--- /dev/null
+++ b/sys.c
@@ -1,0 +1,712 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+#include </sys/src/libc/9syscall/sys.h>
+
+static u32int
+arg(int n)
+{
+	/* no locking necessary, since we're on the stack */
+	return *(u32int*) vaddrnol(P->R[13] + 4 + 4 * n, 4, ARD);
+}
+
+static u32int
+args(int n, Segment **s)
+{
+	/* no locking necessary, since we're on the stack */
+	return *(u32int*) vaddr(P->R[13] + 4 + 4 * n, 4, ARD, s);
+}
+
+static u64int
+argv(int n)
+{
+	return arg(n) | ((u64int)arg(n+1) << 32);
+}
+
+static void
+sysopen(void)
+{
+	u32int name, flags;
+	char *namet;
+	int fd, copied;
+	
+	name = arg(0);
+	flags = arg(1);
+	namet = copyifnec(name, -1, &copied);
+	if(systrace)
+		fprint(2, "open(%#ux=\"%s\", %#o)\n", name, namet, flags);
+	fd = open(namet, flags);
+	if(copied)
+		free(namet);
+	if(fd < 0) {
+		noteerr(0, 1);
+		P->R[0] = fd;
+		return;
+	}
+	setcexec(P->fd, fd, flags & OCEXEC);
+	P->R[0] = fd;
+}
+
+static void
+syscreate(void)
+{
+	u32int name, flags, perm;
+	char *namet;
+	int fd, copied;
+	
+	name = arg(0);
+	flags = arg(1);
+	perm = arg(2);
+	namet = copyifnec(name, -1, &copied);
+	if(systrace)
+		fprint(2, "create(%#ux=\"%s\", %#o, %o)\n", name, namet, flags, perm);
+	fd = create(namet, flags, perm);
+	if(copied)
+		free(namet);
+	if(fd < 0) {
+		noteerr(0, 1);
+		P->R[0] = fd;
+		return;
+	}
+	setcexec(P->fd, fd, flags & OCEXEC);
+	P->R[0] = fd;
+}
+
+static void
+sysclose(void)
+{
+	u32int fd;
+	
+	fd = arg(0);
+	if(systrace)
+		fprint(2, "close(%d)\n", fd);
+	P->R[0] = noteerr(close(fd), 0);
+	if((fd & (1<<31)) == 0)
+		setcexec(P->fd, fd, 0);
+}
+
+static void
+syspread(void)
+{
+	int buffered;
+	u32int fd, size, buf;
+	u64int off;
+	void *targ;
+	Segment *seg;
+	
+	fd = args(0, &seg);
+	buf = arg(1);
+	size = arg(2);
+	off = argv(3);
+	if(systrace)
+		fprint(2, "pread(%d, %#ux, %ud, %#ullx)\n", fd, buf, size, off);
+	targ = bufifnec(buf, size, &buffered);
+	P->R[0] = noteerr(pread(fd, targ, size, off), size);
+	if(buffered)
+		copyback(buf, P->R[0], targ);
+	if(check)
+		markvalid(seg, buf-seg->start, P->R[0]);
+}
+
+static void
+syspwrite(void)
+{
+	u32int fd, size, buf;
+	u64int off;
+	int copied;
+	void *buft;
+	
+	fd = arg(0);
+	buf = arg(1);
+	size = arg(2);
+	off = argv(3);
+	buft = copyifnec(buf, size, &copied);
+	if(systrace)
+		fprint(2, "pwrite(%d, %#ux, %ud, %#ullx)\n", fd, buf, size, off);
+	P->R[0] = noteerr(pwrite(fd, buft, size, off), size);
+	if(copied)
+		free(buft);
+}
+
+static void
+sysseek(void)
+{
+	u32int fd, type;
+	vlong n, *ret;
+	Segment *seg;
+	
+	ret = vaddr(arg(0), 8, 0, &seg);
+	fd = arg(1);
+	n = argv(2);
+	type = arg(4);
+	if(systrace)
+		fprint(2, "seek(%d, %lld, %d)\n", fd, n, type);
+	*ret = seek(fd, n, type);
+	if(*ret < 0) noteerr(0, 1);
+	segunlock(seg);
+}
+
+static void
+sysfd2path(void)
+{
+	u32int fd, buf, nbuf;
+	void *buft;
+	int buffered;
+	
+	fd = arg(0);
+	buf = arg(1);
+	nbuf = arg(2);
+	buft = bufifnec(buf, nbuf, &buffered);
+	if(systrace)
+		fprint(2, "fd2path(%d, %#ux, %d)\n", fd, buf, nbuf);
+	P->R[0] = noteerr(fd2path(fd, buft, nbuf), 0);
+	if(buffered)
+		copyback(buf, nbuf, buft);
+}
+
+static void
+sysstat(void)
+{
+	u32int name, edir, nedir;
+	char *namet;
+	void *edirt;
+	int copied, buffered;
+	
+	name = arg(0);
+	namet = copyifnec(name, -1, &copied);
+	edir = arg(1);
+	nedir = arg(2);
+	edirt = bufifnec(edir, nedir, &buffered);
+	if(systrace)
+		fprint(2, "stat(%#ux=\"%s\", %#ux, %ud)\n", name, namet, edir, nedir);
+	P->R[0] = noteerr(stat(namet, edirt, nedir), nedir);
+	if(copied)
+		free(namet);
+	if(buffered)
+		copyback(edir, P->R[0], edirt);
+}
+
+static void
+sysfstat(void)
+{
+	u32int fd, edir, nedir;
+	void *edirt;
+	int buffered;
+	
+	fd = arg(0);
+	edir = arg(1);
+	nedir = arg(2);
+	edirt = bufifnec(edir, nedir, &buffered);
+	if(systrace)
+		fprint(2, "fstat(%d, %#ux, %d)\n", fd, edir, nedir);
+	P->R[0] = noteerr(fstat(fd, edirt, nedir), nedir);
+	if(buffered)
+		copyback(edir, P->R[0], edirt);
+}
+
+static void
+syswstat(void)
+{
+	u32int name, edir, nedir;
+	char *namet;
+	void *edirt;
+	int copied, copied2;
+	
+	name = arg(0);
+	namet = copyifnec(name, -1, &copied);
+	edir = arg(1);
+	nedir = arg(2);
+	edirt = copyifnec(edir, nedir, &copied2);
+	if(systrace)
+		fprint(2, "wstat(%#ux=\"%s\", %#ux, %ud)\n", name, namet, edir, nedir);
+	P->R[0] = noteerr(wstat(namet, edirt, nedir), nedir);
+	if(copied)
+		free(namet);
+	if(copied2)
+		free(edirt);
+}
+
+static void
+sysfwstat(void)
+{
+	u32int fd, edir, nedir;
+	void *edirt;
+	int copied;
+	
+	fd = arg(0);
+	edir = arg(1);
+	nedir = arg(2);
+	edirt = copyifnec(edir, nedir, &copied);
+	if(systrace)
+		fprint(2, "fwstat(%d, %#ux, %d)\n", fd, edir, nedir);
+	P->R[0] = noteerr(fwstat(fd, edirt, nedir), nedir);
+	if(copied)
+		free(edirt);
+}
+
+static void
+sysexits(void)
+{
+	if(arg(0) == 0)
+		exits(nil);
+	else
+		exits(vaddrnol(arg(0), 0, ARD));
+}
+
+static void
+sysbrk(void)
+{
+	ulong v;
+	vlong o, n;
+	Segment *s;
+	
+	v = arg(0);
+	if(systrace)
+		fprint(2, "brk(%#lux)\n", v);
+	v = v + 7 & -8;
+	if(v >= P->S[SEGSTACK]->start)
+		sysfatal("bss > stack, wtf?");
+	if(v < P->S[SEGBSS]->start)
+		sysfatal("bss length < 0, wtf?");
+	s = P->S[SEGBSS];
+	wlock(&s->rw);
+	s->dref = realloc(s->dref, v - s->start + sizeof(Ref));
+	if(s->dref == nil)
+		sysfatal("error reallocating");
+	s->data = s->dref + 1;
+	if(s->size < v - s->start)
+		memset((char*)s->data + s->size, 0, v - s->start - s->size);
+	if(s->shadow != nil){
+		o = s->size;
+		n = (v - s->start);
+		s->shadow = realloc(s->shadow, n);
+		if(s->shadow == nil)
+			sysfatal("error reallocating");
+		if(n - o > 0)
+			memset(s->shadow + o, 0, n - o);
+	}
+	s->size = v - s->start;
+	P->R[0] = 0;
+	wunlock(&s->rw);
+}
+
+static void
+syserrstr(void)
+{
+	char buf[ERRMAX], *srct;
+	u32int src, len;
+	int copied;
+	
+	src = arg(0);
+	len = arg(1);
+	srct = copyifnec(src, len, &copied);
+	strcpy(buf, P->errbuf);
+	utfecpy(P->errbuf, P->errbuf + ERRMAX, srct);
+	utfecpy(srct, srct + len, buf);
+	if(copied)
+		copyback(src, len, srct);
+	P->R[0] = 0;
+}
+
+static void
+syschdir(void)
+{
+	u32int dir;
+	char *dirt;
+	int copied;
+	
+	dir = arg(0);
+	dirt = copyifnec(dir, -1, &copied);
+	if(systrace)
+		fprint(2, "chdir(%#ux=\"%s\")\n", dir, dirt);
+	P->R[0] = noteerr(chdir(dirt), 0);
+	if(copied)
+		free(dirt);
+}
+
+static void
+sysnotify(void)
+{
+	u32int handler;
+	
+	handler = arg(0);
+	if(systrace)
+		fprint(2, "notify(%#ux)\n", handler);
+	P->notehandler = handler;
+	P->R[0] = 0;
+}
+
+static void
+sysnoted(void)
+{
+	u32int v;
+	
+	v = arg(0);
+	if(systrace)
+		fprint(2, "noted(%d)\n", v);
+	if(P->innote)
+		longjmp(P->notejmp, v + 1);
+	cherrstr("the front fell off");
+	P->R[0] = -1;
+}
+
+static void
+sysrfork(void)
+{
+	u32int flags;
+	int rc, i;
+	Process *p;
+	Segment *s, *t;
+	Fd *old;
+	
+	flags = arg(0);
+	if(systrace)
+		fprint(2, "rfork(%#o)\n", flags);
+	if((flags & (RFFDG | RFCFDG)) == (RFFDG | RFCFDG) ||
+	   (flags & (RFNAMEG | RFCNAMEG)) == (RFNAMEG | RFCNAMEG) ||
+	   (flags & (RFENVG | RFCENVG)) == (RFENVG | RFCENVG)) {
+		P->R[0] = -1;
+		cherrstr("bad arg in syscall");
+		return;
+	}
+	if((flags & RFPROC) == 0) {
+		if(flags & RFFDG) {
+			old = P->fd;
+			P->fd = copyfd(P->fd);
+			fddecref(old);
+		}
+		if(flags & RFCFDG) {
+			old = P->fd;
+			P->fd = newfd();
+			fddecref(old);
+		}
+		P->R[0] = noteerr(rfork(flags), 0);
+		return;
+	}
+	incref(&nproc);
+	p = emallocz(sizeof(Process));
+	memcpy(p, P, sizeof(Process));
+	for(i = 0; i < SEGNUM; i++) {
+		s = p->S[i];
+		if(s == nil)
+			continue;
+		if((flags & RFMEM) == 0 && i != SEGTEXT || i == SEGSTACK) {
+			t = emallocz(sizeof(Segment));
+			incref(t);
+			t->size = s->size;
+			t->start = s->start;
+			t->dref = emalloc(sizeof(Ref) + s->size);
+			memset(t->dref, 0, sizeof(Ref));
+			incref(t->dref);
+			t->data = t->dref + 1;
+			memcpy(t->data, s->data, s->size);
+			p->S[i] = t;
+		} else {
+			incref(s->dref);
+			incref(s);
+		}
+	}
+	
+	if(flags & RFFDG)
+		p->fd = copyfd(P->fd);
+	else if(flags & RFCFDG)
+		p->fd = newfd();
+	else
+		incref(P->fd);
+
+	incref(P->path);
+	rc = rfork(RFMEM | flags);
+	if(rc < 0) /* this should NEVER happen */
+		sysfatal("rfork failed wtf: %r");
+	if(rc == 0) {
+		P = p;
+		atexit(cleanup);
+		P->pid = getpid();
+		inittos();
+		addproc(P);
+	}
+	P->R[0] = rc;
+}
+
+static void
+sysexec(void)
+{
+	u32int name, argv, *argvt;
+	char *namet, **argvv;
+	int i, argc, rc;
+	Segment *seg1, *seg2;
+	
+	name = arg(0);
+	argv = arg(1);
+	namet = strdup(vaddr(name, 0, ARD, &seg1));
+	segunlock(seg1);
+	argvt = vaddr(argv, 0, ARD, &seg1);
+	if(systrace)
+		fprint(2, "exec(%#ux=\"%s\", %#ux)\n", name, namet, argv);
+	for(argc = 0; argvt[argc]; argc++)
+		;
+	argvv = emalloc(sizeof(char *) * argc);
+	for(i = 0; i < argc; i++) {
+		argvv[i] = strdup(vaddr(argvt[i], 0, ARD, &seg2));
+		segunlock(seg2);
+	}
+	segunlock(seg1);
+	rc = loadtext(namet, argc, argvv);
+	for(i = 0; i < argc; i++)
+		free(argvv[i]);
+	free(argvv);
+	if(rc < 0)
+		P->R[0] = noteerr(rc, 0);
+	free(namet);
+}
+
+static void
+sysawait(void)
+{
+	u32int s, n;
+	void *st;
+	int buffered;
+	
+	s = arg(0);
+	n = arg(1);
+	st = bufifnec(s, n, &buffered);
+	if(systrace)
+		fprint(2, "await(%#ux, %d)\n", s, n);
+	P->R[0] = noteerr(await(st, n), 0);
+	if(buffered)
+		copyback(s, P->R[0], st);
+}
+
+static void
+syspipe(void)
+{
+	u32int fd, *fdt;
+	int buffered;
+	
+	fd = arg(0);
+	if(systrace)
+		fprint(2, "pipe(%#ux)\n", fd);
+	fdt = bufifnec(fd, 8, &buffered);
+	P->R[0] = noteerr(pipe((int *) fdt), 0);
+	if(buffered)
+		copyback(fd, 8, fdt);
+}
+
+static void
+sysdup(void)
+{
+	u32int oldfd, newfd;
+	
+	oldfd = arg(0);
+	newfd = arg(1);
+	if(systrace)
+		fprint(2, "dup(%d, %d)\n", oldfd, newfd);
+	P->R[0] = noteerr(dup(oldfd, newfd), 0);
+}
+
+static void
+syssleep(void)
+{
+	u32int n;
+	
+	n = arg(0);
+	if(systrace)
+		fprint(2, "sleep(%d)\n", n);
+	P->R[0] = noteerr(sleep(n), 0);
+}
+
+static void
+sysrendezvous(void)
+{
+	u32int tag, value;
+	
+	tag = arg(0);
+	value = arg(1);
+	if(systrace)
+		fprint(2, "rendezvous(%#ux, %#ux)\n", tag, value);
+	P->R[0] = (u32int) (uintptr)rendezvous((void *) tag, (void *) value);
+	if(P->R[0] == ~0)
+		noteerr(0, 1);
+}
+
+static void
+sysmount(void)
+{
+	u32int fd, afd, old, flag, aname;
+	char *oldt, *anamet;
+	int copiedold, copiedaname;
+	
+	fd = arg(0);
+	afd = arg(1);
+	old = arg(2);
+	flag = arg(3);
+	aname = arg(4);
+	oldt = copyifnec(old, -1, &copiedold);
+	if(aname) {
+		anamet = copyifnec(aname, -1, &copiedaname);
+		if(systrace)
+			fprint(2, "mount(%d, %d, %#x=\"%s\", %#o, %#x=\"%s\")\n", fd, afd, old, oldt, flag, aname, anamet);
+	} else {
+		anamet = nil;
+		copiedaname = 0;
+		if(systrace)
+			fprint(2, "mount(%d, %d, %#x=\"%s\", %#o, nil)\n", fd, afd, old, oldt, flag);
+	}
+	P->R[0] = noteerr(mount(fd, afd, oldt, flag, anamet), 0);
+	if(copiedold)
+		free(oldt);
+	if(copiedaname)
+		free(anamet);
+}
+
+static void
+sysbind(void)
+{
+	u32int name, old, flags;
+	char *namet, *oldt;
+	int copiedname, copiedold;
+	
+	name = arg(0);
+	old = arg(1);
+	flags = arg(2);
+	namet = copyifnec(name, -1, &copiedname);
+	oldt = copyifnec(old, -1, &copiedold);
+	if(systrace)
+		fprint(2, "bind(%#ux=\"%s\", %#ux=\"%s\", %#o)\n", name, namet, old, oldt, flags);
+	P->R[0] = noteerr(bind(namet, oldt, flags), 0);
+	if(copiedname)
+		free(namet);
+	if(copiedold)
+		free(oldt);
+}
+
+static void
+sysunmount(void)
+{
+	u32int name, old;
+	char *namet, *oldt;
+	int copiedname, copiedold;
+	
+	name = arg(0);
+	old = arg(1);
+	oldt = copyifnec(old, -1, &copiedold);
+	if(name == 0) {
+		namet = nil;
+		copiedname = 0;
+		if(systrace)
+			fprint(2, "unmount(nil, %#ux=\"%s\")\n", old, oldt);
+		P->R[0] = noteerr(unmount(nil, oldt), 0);
+	} else {
+		namet = copyifnec(name, -1, &copiedname);
+		if(systrace)
+			fprint(2, "unmount(%#ux=\"%s\", %#ux=\"%s\")\n", name, namet, old, oldt);
+		P->R[0] = noteerr(unmount(namet, oldt), 0);
+	}
+	if(copiedold)
+		free(oldt);
+	if(copiedname)
+		free(namet);
+}
+
+static void
+sysremove(void)
+{
+	u32int file;
+	char *filet;
+	int copied;
+	
+	file = arg(0);
+	filet = copyifnec(file, -1, &copied);
+	if(systrace)
+		fprint(2, "remove(%#ux=\"%s\")\n", file, filet);
+	P->R[0] = noteerr(remove(filet), 0);
+	if(copied)
+		free(filet);
+}
+
+static void
+sysalarm(void)
+{
+	u32int msec;
+	
+	msec = arg(0);
+	if(systrace)
+		fprint(2, "alarm(%d)\n", msec);
+	P->R[0] = alarm(msec);
+}
+
+static void
+syssemacquire(void)
+{
+	u32int addr, block;
+	long *addrt;
+
+	addr = arg(0);
+	block = arg(1);
+	if(systrace)
+		fprint(2, "semacquire(%#ux, %ud)\n", addr, block);
+	addrt = vaddrnol(addr, 4, ARD|AWR);
+	P->R[0] = noteerr(semacquire(addrt, block), 0);
+}
+
+static void
+syssemrelease(void)
+{
+	u32int addr, count;
+	long *addrt;
+	Segment *seg;
+
+	addr = arg(0);
+	count = arg(1);
+	if(systrace)
+		fprint(2, "semrelease(%#ux, %ud)\n", addr, count);
+	addrt = vaddr(addr, 4, ARD|AWR, &seg);
+	P->R[0] = noteerr(semrelease(addrt, count), 0);
+	segunlock(seg);
+}
+
+void
+syscall(void)
+{
+	u32int n;
+	static void (*calls[])(void) = {
+		[EXITS] sysexits,
+		[CLOSE] sysclose,
+		[OPEN] sysopen,
+		[CREATE] syscreate,
+		[PREAD] syspread,
+		[PWRITE] syspwrite,
+		[BRK_] sysbrk,
+		[ERRSTR] syserrstr,
+		[STAT] sysstat,
+		[FSTAT] sysfstat,
+		[WSTAT] syswstat,
+		[FWSTAT] sysfwstat,
+		[SEEK] sysseek,
+		[CHDIR] syschdir,
+		[FD2PATH] sysfd2path,
+		[NOTIFY] sysnotify,
+		[NOTED] sysnoted,
+		[RFORK] sysrfork,
+		[EXEC] sysexec,
+		[AWAIT] sysawait,
+		[PIPE] syspipe,
+		[SLEEP] syssleep,
+		[RENDEZVOUS] sysrendezvous,
+		[BIND] sysbind,
+		[UNMOUNT] sysunmount,
+		[DUP] sysdup,
+		[MOUNT] sysmount,
+		[REMOVE] sysremove,
+		[ALARM] sysalarm,
+		[SEMACQUIRE] syssemacquire,
+		[SEMRELEASE] syssemrelease,
+	};
+	
+	n = P->R[0];
+	if(n >= nelem(calls) || calls[n] == nil)
+		sysfatal("no such syscall %d @ %#ux", n, P->R[15] - 4);
+	calls[n]();
+}
--- /dev/null
+++ b/test.c
@@ -1,0 +1,18 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char **argv)
+{
+	char *x;
+	int a[1024];
+	int y;
+
+	x = malloc(123);
+	x[1] = 123;
+	y = x[1];
+	argv[0][0] = y;
+	free(x);
+	argv[0][0] = x[0];
+	exits(x);
+}
--- /dev/null
+++ b/util.c
@@ -1,0 +1,40 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+void *
+emalloc(u32int size)
+{
+	void *v;
+	
+	v = malloc(size);
+	if(v == nil)
+		sysfatal("%r");
+	setmalloctag(v, getcallerpc(&size));
+	return v;
+}
+
+void *
+emallocz(u32int size)
+{
+	void *v;
+	
+	v = emalloc(size);
+	memset(v, 0, size);
+	setmalloctag(v, getcallerpc(&size));
+	return v;
+}
+
+void *
+erealloc(void *old, u32int size)
+{
+	void *v;
+	
+	v = realloc(old, size);
+	if(v == nil)
+		sysfatal("%r");
+	setrealloctag(v, getcallerpc(&old));
+	return v;
+}
--- /dev/null
+++ b/vfp.c
@@ -1,0 +1,160 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+#include <mach.h>
+#include "dat.h"
+#include "fns.h"
+
+void
+resetvfp(void)
+{
+	int i;
+
+	P->FPSR = 0x00000000;
+	for(i = 0; i < Nfpregs; i++)
+		P->F[i] = 0;
+}
+
+void
+vfpregtransfer(u32int instr)
+{
+	u32int *Rt;
+	long double *Fn;
+	Rt = P->R + ((instr>>12)&0xF);
+	Fn = P->F + ((instr>>16)&0xF);
+
+	switch((instr>>20)&0xF){
+	case 0:
+		*((int*)Fn) = *Rt; break;
+	case 1:
+		*Rt = *((int*)Fn); break;
+	case 14:
+		P->FPSR = *Rt; break;
+	case 15:
+		if(Rt == (P->R + 15))
+			P->CPSR = P->FPSR;
+		else
+			*Rt = P->FPSR;
+		break;
+	default:
+		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, P->R[15] - 4);
+	}
+}
+
+void
+vfprmtransfer(u32int instr)
+{
+	int m, n, d, off, sz;
+	void* ea;
+	Segment *seg;
+
+	n = (instr>>16) & 0xF;
+	d = (instr>>12) & 0xF;
+	off = (instr & 0xFF) << 2;
+	sz = instr & (1<<8);
+	if((instr & (1<<23)) == 0)
+		off = -off;
+	m = (instr>>20)&0x3;
+	ea = vaddr(evenaddr(P->R[n] + off, 3), sz ? 8 : 4, m?ARD:AWR, &seg);
+	switch(m){
+	case 0:
+		if(sz)
+			*(double*)ea = P->F[d];
+		else
+			*(float*)ea = P->F[d];
+		break;
+	case 1:
+		if(sz)
+			P->F[d] = *(double*)ea;
+		else
+			P->F[d] = *(float*)ea;
+		break;
+	default:
+		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, P->R[15] - 4);
+	}
+	segunlock(seg);
+}
+
+void
+vfparithop(int opc, u32int instr)
+{
+	int o;
+	long double *Fd, *Fn, *Fm;
+	Fd = P->F + ((instr>>12)&0xF);
+	Fn = P->F + ((instr>>16)&0xF);
+	Fm = P->F + (instr&0xF);
+	o = ((opc&0x3)<<1) | (opc&0x8) | ((instr>>6)&0x1);
+
+	switch(o){
+	case 4:
+		*Fd = *Fn * *Fm; break;
+	case 6:
+		*Fd = *Fn + *Fm; break;
+	case 7:
+		*Fd = *Fn - *Fm; break;
+	case 8:
+		*Fd = *Fn / *Fm; break;
+	default:
+		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, P->R[15] - 4);
+	}
+}
+
+void
+vfpotherop(u32int instr)
+{
+	int o2, o3;
+	long double *Fd, *Fm, F0;
+	Fd = P->F + ((instr>>12)&0xF);
+	Fm = P->F + (instr&0xF);
+	F0 = 0.0;
+	o2 = (instr>>16) & 0xF;
+	o3 = (instr>>6) & 0x3;
+
+	if((o3&1) == 0)
+		sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, P->R[15] - 4);
+	switch(o2){
+	case 0x5:
+		Fm = &F0;
+	case 0x4:
+		if(*Fd < *Fm)
+			P->FPSR = (P->FPSR & ~FLAGS) | flN;
+		else if(*Fd >= *Fm) {
+			P->FPSR = (P->FPSR & ~FLAGS) | flC;
+			if(*Fd == *Fm)
+				P->FPSR |= flZ;
+		} else
+			P->FPSR = (P->FPSR & ~FLAGS) | flV | flC;
+		break;
+	case 0x8:
+		*Fd = *((int*)Fm); break;
+	case 0xD:
+		*((int*)Fd) = (int)*Fm; break;
+	default:
+		switch((o2<<1)|(o3>>1)){
+		case 0:
+		case 15:
+			*Fd = *Fm; break;
+		case 1:
+			*Fd = fabs(*Fm); break;
+		case 2:
+			*Fd = -*Fm; break;
+		case 3:
+			*Fd = sqrt(*Fm); break;
+		default:
+			sysfatal("unimplemented VFP instruction %8ux @ %8ux", instr, P->R[15] - 4);
+		}
+	}
+}
+
+void
+vfpoperation(u32int instr)
+{
+	int o1;
+	o1 = (instr>>20) & 0xF;
+	if(o1 == 0xB)
+		vfpotherop(instr);
+	else
+		vfparithop(o1, instr);
+}
+