diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h
index 65b483fb227a36ebee868c9875095c596707d092..39048e37d710380bc14efa6c2ba38db9ae603c55 100644
--- a/src/cmd/dist/a.h
+++ b/src/cmd/dist/a.h
@@ -37,10 +37,13 @@ enum {
 
 // buf.c
 bool	bequal(Buf *s, Buf *t);
+void	bsubst(Buf *b, char *x, char *y);
 void	bfree(Buf *b);
 void	bgrow(Buf *b, int n);
 void	binit(Buf *b);
+char*	bpathf(Buf *b, char *fmt, ...);
 char*	bprintf(Buf *b, char *fmt, ...);
+void	bwritef(Buf *b, char *fmt, ...);
 void	breset(Buf *b);
 char*	bstr(Buf *b);
 char*	btake(Buf *b);
@@ -62,23 +65,41 @@ void	splitfields(Vec*, char*);
 extern char *default_goroot;
 extern char *goarch;
 extern char *gobin;
+extern char *gochar;
 extern char *gohostarch;
 extern char *gohostos;
 extern char *goos;
 extern char *goroot;
+extern char *goversion;
 extern char *workdir;
 extern char *slash;
 
+int	find(char*, char**, int);
 void	init(void);
+void	cmdbanner(int, char**);
 void	cmdbootstrap(int, char**);
+void	cmdclean(int, char**);
 void	cmdenv(int, char**);
 void	cmdinstall(int, char**);
+void	cmdversion(int, char**);
 
 // buildgc.c
 void	gcopnames(char*, char*);
 void	mkenam(char*, char*);
 
+// buildruntime.c
+void	mkzasm(char*, char*);
+void	mkzgoarch(char*, char*);
+void	mkzgoos(char*, char*);
+void	mkzruntimedefs(char*, char*);
+void	mkzversion(char*, char*);
+
+// goc2c.c
+void	goc2c(char*, char*);
+
 // main.c
+extern int vflag;
+void	usage(void);
 void	xmain(int argc, char **argv);
 
 // portability layer (plan9.c, unix.c, windows.c)
@@ -94,6 +115,8 @@ Time	mtime(char*);
 void	readfile(Buf*, char*);
 void	run(Buf *b, char *dir, int mode, char *cmd, ...);
 void	runv(Buf *b, char *dir, int mode, Vec *argv);
+void	bgrunv(char *dir, int mode, Vec *argv);
+void	bgwait(void);
 bool	streq(char*, char*);
 void	writefile(Buf*, char*);
 void	xatexit(void (*f)(void));
@@ -118,7 +141,6 @@ void	xremoveall(char *p);
 void	xsetenv(char*, char*);
 int	xstrcmp(char*, char*);
 char*	xstrdup(char *p);
-int	xstreq(char*, char*);
 int	xstrlen(char*);
 char*	xstrrchr(char*, int);
 char*	xstrstr(char*, char*);
diff --git a/src/cmd/dist/arg.h b/src/cmd/dist/arg.h
new file mode 100644
index 0000000000000000000000000000000000000000..6eef0353be6c146c8a1ab394fa85f57a409a62ef
--- /dev/null
+++ b/src/cmd/dist/arg.h
@@ -0,0 +1,50 @@
+/*
+Derived from Inferno include/kern.h.
+
+http://code.google.com/p/inferno-os/source/browse/include/kern.h
+
+	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+	Revisions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).  All rights reserved.
+	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* command line */
+extern char	*argv0;
+#define	ARGBEGIN	for((argv0?0:(argv0=(*argv))),argv++,argc--;\
+			    argv[0] && argv[0][0]=='-' && argv[0][1];\
+			    argc--, argv++) {\
+				char *_args, *_argt;\
+				char _argc;\
+				_args = &argv[0][1];\
+				if(_args[0]=='-' && _args[1]==0){\
+					argc--; argv++; break;\
+				}\
+				_argc = 0;\
+				while((_argc = *_args++) != 0)\
+				switch(_argc)
+#define	ARGEND		_argt=0;USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
+#define	ARGF()		(_argt=_args, _args="",\
+				(*_argt? _argt: argv[1]? (argc--, *++argv): 0))
+#define	EARGF(x)	(_argt=_args, _args="",\
+				(*_argt? _argt: argv[1]? (argc--, *++argv): ((x), fatal("usage"), (char*)0)))
+
+#define	ARGC()		_argc
+
diff --git a/src/cmd/dist/buf.c b/src/cmd/dist/buf.c
index b05561d3826a6c58d8ad25cdc1aea2590e9a3204..c7a7c1277c78a7931ffc74a003d6d6ebc5c18c90 100644
--- a/src/cmd/dist/buf.c
+++ b/src/cmd/dist/buf.c
@@ -99,6 +99,32 @@ bequal(Buf *s, Buf *t)
 	return s->len == t->len && xmemcmp(s->p, t->p, s->len) == 0;
 }
 
+// bsubst rewites b to replace all occurrences of x with y.
+void
+bsubst(Buf *b, char *x, char *y)
+{
+	char *p;
+	int nx, ny, pos;
+
+	nx = xstrlen(x);
+	ny = xstrlen(y);
+
+	pos = 0;
+	for(;;) {
+		p = xstrstr(bstr(b)+pos, x);
+		if(p == nil)
+			break;
+		if(nx != ny) {
+			if(nx < ny)
+				bgrow(b, ny-nx);
+			xmemmove(p+ny, p+nx, (b->p+b->len)-(p+nx));
+		}
+		xmemmove(p, y, ny);
+		pos = p+ny - b->p;
+		b->len += ny - nx;
+	}
+}
+
 // The invariant with the vectors is that v->p[0:v->len] is allocated
 // strings that are owned by the vector.  The data beyond v->len may
 // be garbage.
@@ -214,7 +240,7 @@ vuniq(Vec *v)
 void
 splitlines(Vec *v, char *p)
 {
-	int i, c;
+	int i;
 	char *start;
 	
 	vreset(v);
diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index 17f491af8a16fcc6b682632b0196589340f54925..f2b25d991ebfdc2ac115d5b5011f0c7f234cc385 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 #include "a.h"
+#include "arg.h"
 
 /*
  * Initialization for any invocation.
@@ -18,13 +19,13 @@ char *goroot;
 char *workdir;
 char *gochar;
 char *goroot_final;
-char *goversion = "go1";  // TODO: Read correct version
+char *goversion;
 char *slash;	// / for unix, \ for windows
 char *default_goroot;
 
-static void fixslash(Buf*);
 static bool shouldbuild(char*, char*);
 static void copy(char*, char*);
+static char *findgoversion(void);
 
 // The known architecture letters.
 static char *gochars = "568";
@@ -51,7 +52,7 @@ static char *okgoos[] = {
 static void rmworkdir(void);
 
 // find reports the first index of p in l[0:n], or else -1.
-static int
+int
 find(char *p, char **l, int n)
 {
 	int i;
@@ -92,14 +93,13 @@ init(void)
 	if(find(goos, okgoos, nelem(okgoos)) < 0)
 		fatal("unknown $GOOS %s", goos);
 
-	p = bprintf(&b, "%s/include/u.h", goroot);
-	fixslash(&b);
+	p = bpathf(&b, "%s/include/u.h", goroot);
 	if(!isfile(p)) {
 		fatal("$GOROOT is not set correctly or not exported\n"
 			"\tGOROOT=%s\n"
 			"\t%s does not exist", goroot, p);
 	}
-	
+
 	xgetenv(&b, "GOHOSTARCH");
 	if(b.len > 0)
 		gohostarch = btake(&b);
@@ -130,6 +130,8 @@ init(void)
 	xsetenv("LANG", "C");
 	xsetenv("LANGUAGE", "en_US.UTF8");
 
+	goversion = findgoversion();
+
 	workdir = xworkdir();
 	xatexit(rmworkdir);
 
@@ -140,10 +142,115 @@ init(void)
 static void
 rmworkdir(void)
 {
-	xprintf("rm -rf %s\n", workdir);
+	if(vflag > 1)
+		xprintf("rm -rf %s\n", workdir);
 	xremoveall(workdir);
 }
 
+// Remove trailing spaces.
+static void
+chomp(Buf *b)
+{
+	int c;
+
+	while(b->len > 0 && ((c=b->p[b->len-1]) == ' ' || c == '\t' || c == '\r' || c == '\n'))
+		b->len--;
+}
+
+
+// findgoversion determines the Go version to use in the version string.
+static char*
+findgoversion(void)
+{
+	char *tag, *rev, *p;
+	int i, nrev;
+	Buf b, path, bmore, branch;
+	Vec tags;
+	
+	binit(&b);
+	binit(&path);
+	binit(&bmore);
+	binit(&branch);
+	vinit(&tags);
+	
+	// The $GOROOT/VERSION file takes priority, for distributions
+	// without the Mercurial repo.
+	bpathf(&path, "%s/VERSION", goroot);
+	if(isfile(bstr(&path))) {
+		readfile(&b, bstr(&path));
+		chomp(&b);
+		goto done;
+	}
+
+	// The $GOROOT/VERSION.cache file is a cache to avoid invoking
+	// hg every time we run this command.  Unlike VERSION, it gets
+	// deleted by the clean command.
+	bpathf(&path, "%s/VERSION.cache", goroot);
+	if(isfile(bstr(&path))) {
+		readfile(&b, bstr(&path));
+		chomp(&b);
+		goto done;
+	}
+
+	// Otherwise, use Mercurial.
+	// What is the current branch?
+	run(&branch, goroot, CheckExit, "hg", "identify", "-b", nil);
+	chomp(&branch);
+
+	// What are the tags along the current branch?
+	tag = "";
+	rev = ".";
+	run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "--template", "{tags} + ", nil);
+	splitfields(&tags, bstr(&b));
+	nrev = 0;
+	for(i=0; i<tags.len; i++) {
+		p = tags.p[i];
+		if(streq(p, "+"))
+			nrev++;
+		if(hasprefix(p, "release.") || hasprefix(p, "weekly.") || hasprefix(p, "go")) {
+			tag = xstrdup(p);
+			// If this tag matches the current checkout
+			// exactly (no "+" yet), don't show extra
+			// revision information.
+			if(nrev == 0)
+				rev = "";
+			break;
+		}
+	}
+
+	if(tag[0] == '\0') {
+		// Did not find a tag; use branch name.
+		bprintf(&b, "branch.%s", bstr(&branch));
+		tag = btake(&b);
+	}
+	
+	if(rev[0]) {
+		// Tag is before the revision we're building.
+		// Add extra information.
+		run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil);
+		chomp(&bmore);
+	}
+	
+	bprintf(&b, "%s", tag);
+	if(bmore.len > 0)
+		bwriteb(&b, &bmore);
+
+	// Cache version.
+	writefile(&b, bstr(&path));
+
+done:
+	p = btake(&b);
+	
+	
+	bfree(&b);
+	bfree(&path);
+	bfree(&bmore);
+	bfree(&branch);
+	vfree(&tags);
+	
+	return p;
+}
+
 /*
  * Initial tree setup.
  */
@@ -180,33 +287,26 @@ setup(void)
 
 	binit(&b);
 
-	run(&b, nil, 0, "ld", "--version", nil);
-	if(contains(bstr(&b), "gold") && contains(bstr(&b), " 2.20")) {
-		fatal("Your system has gold 2.20 installed.\n"
-			"This version is shipped by Ubuntu even though\n"
-			"it is known not to work on Ubuntu.\n"
-			"Binaries built with this linker are likely to fail in mysterious ways.\n"
-			"\n"
-			"Run sudo apt-get remove binutils-gold.");
-	}
-
 	// Create tool directory.
-	p = bprintf(&b, "%s/bin", goroot);
-	fixslash(&b);
+	p = bpathf(&b, "%s/bin", goroot);
 	if(!isdir(p))
 		xmkdir(p);
-	p = bprintf(&b, "%s/bin/go-tool", goroot);
-	fixslash(&b);
+	p = bpathf(&b, "%s/bin/tool", goroot);
 	if(!isdir(p))
 		xmkdir(p);
 
 	// Create package directory.
-	p = bprintf(&b, "%s/pkg", goroot);
-	fixslash(&b);
+	p = bpathf(&b, "%s/pkg", goroot);
 	if(!isdir(p))
 		xmkdir(p);
-	p = bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
-	fixslash(&b);
+	p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
+	xremoveall(p);
+	xmkdir(p);
+
+	// Create object directory.
+	// We keep it in pkg/ so that all the generated binaries
+	// are in one tree.
+	p = bpathf(&b, "%s/pkg/obj", goroot);
 	xremoveall(p);
 	xmkdir(p);
 
@@ -261,10 +361,6 @@ static struct {
 		"$GOROOT/include/libc.h",
 		"fmt/*",
 		"utf/*",
-		"-utf/mkrunetype",
-		"-utf\\mkrunetype",
-		"-utf/runetypebody",
-		"-utf\\runetypebody",
 	}},
 	{"libbio", {
 		"$GOROOT/include/u.h",
@@ -300,37 +396,37 @@ static struct {
 		"../cc/pgen.c",
 		"../cc/pswt.c",
 		"../5l/enam.c",
-		"$GOROOT/lib/libcc.a",
+		"$GOROOT/pkg/obj/libcc.a",
 	}},
 	{"cmd/6c", {
 		"../cc/pgen.c",
 		"../cc/pswt.c",
 		"../6l/enam.c",
-		"$GOROOT/lib/libcc.a",
+		"$GOROOT/pkg/obj/libcc.a",
 	}},
 	{"cmd/8c", {
 		"../cc/pgen.c",
 		"../cc/pswt.c",
 		"../8l/enam.c",
-		"$GOROOT/lib/libcc.a",
+		"$GOROOT/pkg/obj/libcc.a",
 	}},
 	{"cmd/5g", {
 		"../gc/cplx.c",
 		"../gc/pgen.c",
 		"../5l/enam.c",
-		"$GOROOT/lib/libgc.a",
+		"$GOROOT/pkg/obj/libgc.a",
 	}},
 	{"cmd/6g", {
 		"../gc/cplx.c",
 		"../gc/pgen.c",
 		"../6l/enam.c",
-		"$GOROOT/lib/libgc.a",
+		"$GOROOT/pkg/obj/libgc.a",
 	}},
 	{"cmd/8g", {
 		"../gc/cplx.c",
 		"../gc/pgen.c",
 		"../8l/enam.c",
-		"$GOROOT/lib/libgc.a",
+		"$GOROOT/pkg/obj/libgc.a",
 	}},
 	{"cmd/5l", {
 		"../ld/*",
@@ -345,9 +441,16 @@ static struct {
 		"enam.c",
 	}},
 	{"cmd/", {
-		"$GOROOT/lib/libmach.a",
-		"$GOROOT/lib/libbio.a",
-		"$GOROOT/lib/lib9.a",
+		"$GOROOT/pkg/obj/libmach.a",
+		"$GOROOT/pkg/obj/libbio.a",
+		"$GOROOT/pkg/obj/lib9.a",
+	}},
+	{"pkg/runtime", {
+		"zasm_$GOOS_$GOARCH.h",
+		"zgoarch_$GOARCH.go",
+		"zgoos_$GOOS.go",
+		"zruntime_defs_$GOOS_$GOARCH.go",
+		"zversion.go",
 	}},
 };
 
@@ -357,15 +460,21 @@ char *depsuffix[] = {
 	".h",
 	".s",
 	".go",
+	".goc",
 };
 
 // gentab records how to generate some trivial files.
 static struct {
-	char *name;
+	char *nameprefix;
 	void (*gen)(char*, char*);
 } gentab[] = {
 	{"opnames.h", gcopnames},
 	{"enam.c", mkenam},
+	{"zasm_", mkzasm},
+	{"zgoarch_", mkzgoarch},
+	{"zgoos_", mkzgoos},
+	{"zruntime_defs_", mkzruntimedefs},
+	{"zversion.go", mkzversion},
 };
 
 // install installs the library, package, or binary associated with dir,
@@ -373,7 +482,7 @@ static struct {
 static void
 install(char *dir)
 {
-	char *name, *p, *elem, *prefix;
+	char *name, *p, *elem, *prefix, *exe;
 	bool islib, ispkg, isgo, stale;
 	Buf b, b1, path;
 	Vec compile, files, link, go, missing, clean, lib, extra;
@@ -393,14 +502,16 @@ install(char *dir)
 	vinit(&extra);
 	
 	// path = full path to dir.
-	bprintf(&path, "%s/src/%s", goroot, dir);
-	fixslash(&path);
+	bpathf(&path, "%s/src/%s", goroot, dir);
 	name = lastelem(dir);
 
 	islib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");
 	ispkg = hasprefix(dir, "pkg");
 	isgo = ispkg || streq(dir, "cmd/go");
 
+	exe = "";
+	if(streq(gohostos, "windows"))
+		exe = ".exe";
 	
 	// Start final link command line.
 	// Note: code below knows that link.p[2] is the target.
@@ -411,37 +522,25 @@ install(char *dir)
 		prefix = "";
 		if(!hasprefix(name, "lib"))
 			prefix = "lib";
-		bprintf(&b, "%s/lib/%s%s.a", goroot, prefix, name);
-		fixslash(&b);
-		vadd(&link, bstr(&b));
+		vadd(&link, bpathf(&b, "%s/pkg/obj/%s%s.a", goroot, prefix, name));
 	} else if(ispkg) {
 		// Go library (package).
-		bprintf(&b, "%s/bin/go-tool/pack", goroot);
-		fixslash(&b);
-		vadd(&link, bstr(&b));
+		vadd(&link, bpathf(&b, "%s/bin/tool/pack", goroot));
 		vadd(&link, "grc");
 		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
 		*xstrrchr(p, '/') = '\0';
 		xmkdirall(p);
-		bprintf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4);
-		fixslash(&b);
-		vadd(&link, bstr(&b));
+		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));
 	} else if(streq(dir, "cmd/go")) {
 		// Go command.
-		bprintf(&b, "%s/bin/go-tool/%sl", goroot, gochar);
-		fixslash(&b);
-		vadd(&link, bstr(&b));
+		vadd(&link, bpathf(&b, "%s/bin/tool/%sl", goroot, gochar));
 		vadd(&link, "-o");
-		bprintf(&b, "%s/bin/go-tool/go_bootstrap", goroot);
-		fixslash(&b);
-		vadd(&link, bstr(&b));
+		vadd(&link, bpathf(&b, "%s/bin/tool/go_bootstrap%s", goroot, exe));
 	} else {
 		// C command.
 		vadd(&link, "gcc");
 		vadd(&link, "-o");
-		bprintf(&b, "%s/bin/go-tool/%s", goroot, name);
-		fixslash(&b);
-		vadd(&link, bstr(&b));
+		vadd(&link, bpathf(&b, "%s/bin/tool/%s%s", goroot, name, exe));
 	}
 	ttarg = mtime(link.p[2]);
 
@@ -452,26 +551,24 @@ install(char *dir)
 	for(i=0; i<nelem(deptab); i++) {
 		if(hasprefix(dir, deptab[i].prefix)) {
 			for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
-				if(hasprefix(p, "$GOROOT/")) {
-					bprintf(&b1, "%s/%s", goroot, p+8);
-					p = bstr(&b1);
-				}
+				breset(&b1);
+				bwritestr(&b1, p);
+				bsubst(&b1, "$GOROOT", goroot);
+				bsubst(&b1, "$GOOS", goos);
+				bsubst(&b1, "$GOARCH", goarch);
+				p = bstr(&b1);
 				if(hassuffix(p, ".a")) {
-					vadd(&lib, p);
+					vadd(&lib, bpathf(&b, "%s", p));
 					continue;
 				}
 				if(hassuffix(p, "/*")) {
-					bprintf(&b, "%s/%s", bstr(&path), p);
+					bpathf(&b, "%s/%s", bstr(&path), p);
 					b.len -= 2;
-					fixslash(&b);
 					xreaddir(&extra, bstr(&b));
 					bprintf(&b, "%s", p);
 					b.len -= 2;
-					for(k=0; k<extra.len; k++) {
-						bprintf(&b1, "%s/%s", bstr(&b), extra.p[k]);
-						fixslash(&b1);
-						vadd(&files, bstr(&b1));
-					}
+					for(k=0; k<extra.len; k++)
+						vadd(&files, bpathf(&b1, "%s/%s", bstr(&b), extra.p[k]));
 					continue;
 				}
 				if(hasprefix(p, "-")) {
@@ -495,28 +592,12 @@ install(char *dir)
 	// Convert to absolute paths.
 	for(i=0; i<files.len; i++) {
 		if(!isabs(files.p[i])) {
-			bprintf(&b, "%s/%s", bstr(&path), files.p[i]);
-			fixslash(&b);
+			bpathf(&b, "%s/%s", bstr(&path), files.p[i]);
 			xfree(files.p[i]);
 			files.p[i] = btake(&b);
 		}
 	}
 
-	// For package runtime, copy some files into the work space.
-	if(streq(dir, "pkg/runtime")) {		
-		copy(bprintf(&b, "%s/arch_GOARCH.h", workdir),
-			bprintf(&b1, "%s/arch_%s.h", bstr(&path), goarch));
-		copy(bprintf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
-			bprintf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch));
-		copy(bprintf(&b, "%s/os_GOOS.h", workdir),
-			bprintf(&b1, "%s/os_%s.h", bstr(&path), goos));
-		copy(bprintf(&b, "%s/signals_GOOS.h", workdir),
-			bprintf(&b1, "%s/signals_%s.h", bstr(&path), goos));
-		copy(bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
-			bprintf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch));
-	}
-
-
 	// Is the target up-to-date?
 	stale = 1;  // TODO: Decide when 0 is okay.
 	n = 0;
@@ -529,6 +610,12 @@ install(char *dir)
 		continue;
 	ok:
 		t = mtime(p);
+		if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) {
+			xfree(files.p[i]);
+			continue;
+		}
+		if(hassuffix(p, ".go"))
+			vadd(&go, p);
 		if(t > ttarg)
 			stale = 1;
 		if(t == 0) {
@@ -536,12 +623,6 @@ install(char *dir)
 			files.p[n++] = files.p[i];
 			continue;
 		}
-		if(!hassuffix(p, ".a") && !shouldbuild(p, dir)) {
-			xfree(files.p[i]);
-			continue;
-		}
-		if(hassuffix(p, ".go"))
-			vadd(&go, p);
 		files.p[n++] = files.p[i];
 	}
 	files.len = n;
@@ -553,21 +634,67 @@ install(char *dir)
 	if(!stale)
 		goto out;
 
-	// Generate any missing files.
-	for(i=0; i<missing.len; i++) {
-		p = missing.p[i];
+	// For package runtime, copy some files into the work space.
+	if(streq(dir, "pkg/runtime")) {
+		copy(bpathf(&b, "%s/arch_GOARCH.h", workdir),
+			bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch));
+		copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir),
+			bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch));
+		copy(bpathf(&b, "%s/os_GOOS.h", workdir),
+			bpathf(&b1, "%s/os_%s.h", bstr(&path), goos));
+		copy(bpathf(&b, "%s/signals_GOOS.h", workdir),
+			bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos));
+	}
+
+	// Generate any missing files; regenerate existing ones.
+	for(i=0; i<files.len; i++) {
+		p = files.p[i];
 		elem = lastelem(p);
 		for(j=0; j<nelem(gentab); j++) {
-			if(streq(gentab[j].name, elem)) {
+			if(hasprefix(elem, gentab[j].nameprefix)) {
+				if(vflag > 1)
+					xprintf("generate %s\n", p);
 				gentab[j].gen(bstr(&path), p);
-				vadd(&clean, p);
+				// Do not add generated file to clean list.
+				// In pkg/runtime, we want to be able to
+				// build the package with the go tool,
+				// and it assumes these generated files already
+				// exist (it does not know how to build them).
+				// The 'clean' command can remove
+				// the generated files.
 				goto built;
 			}
 		}
-		fatal("missing file %s", p);
+		// Did not rebuild p.
+		if(find(p, missing.p, missing.len) >= 0)
+			fatal("missing file %s", p);
 	built:;
 	}
 
+	// One more copy for package runtime.
+	// The last batch was required for the generators.
+	// This one is generated.
+	if(streq(dir, "pkg/runtime")) {
+		copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir),
+			bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch));
+	}
+	
+	// Generate .c files from .goc files.
+	if(streq(dir, "pkg/runtime")) {		
+		for(i=0; i<files.len; i++) {
+			p = files.p[i];
+			if(!hassuffix(p, ".goc"))
+				continue;
+			// b = path/zp but with _goarch.c instead of .goc
+			bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p));
+			b.len -= 4;
+			bwritef(&b, "_%s.c", goarch);
+			goc2c(p, bstr(&b));
+			vadd(&files, bstr(&b));
+		}
+		vuniq(&files);
+	}
+
 	// Compile the files.
 	for(i=0; i<files.len; i++) {
 		if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
@@ -585,10 +712,8 @@ install(char *dir)
 			if(streq(dir, "lib9"))
 				vadd(&compile, "-DPLAN9PORT");
 	
-			bprintf(&b, "%s/include", goroot);
-			fixslash(&b);
 			vadd(&compile, "-I");
-			vadd(&compile, bstr(&b));
+			vadd(&compile, bpathf(&b, "%s/include", goroot));
 			
 			vadd(&compile, "-I");
 			vadd(&compile, bstr(&path));
@@ -597,7 +722,9 @@ install(char *dir)
 			if(streq(name, "goos.c")) {
 				vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos));
 				vadd(&compile, bprintf(&b, "-DGOARCH=\"%s\"", goarch));
-				vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", goroot));
+				bprintf(&b1, "%s", goroot);
+				bsubst(&b1, "\\", "\\\\");  // turn into C string
+				vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1)));
 				vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion));
 			}
 	
@@ -608,14 +735,10 @@ install(char *dir)
 			}
 		} else {
 			// Supporting files for a Go package.
-			if(hassuffix(files.p[i], ".s")) {
-				bprintf(&b, "%s/bin/go-tool/%sa", goroot, gochar);
-				fixslash(&b);
-				vadd(&compile, bstr(&b));
-			} else {
-				bprintf(&b, "%s/bin/go-tool/%sc", goroot, gochar);
-				fixslash(&b);
-				vadd(&compile, bstr(&b));
+			if(hassuffix(files.p[i], ".s"))
+				vadd(&compile, bpathf(&b, "%s/bin/tool/%sa", goroot, gochar));
+			else {
+				vadd(&compile, bpathf(&b, "%s/bin/tool/%sc", goroot, gochar));
 				vadd(&compile, "-FVw");
 			}
 			vadd(&compile, "-I");
@@ -624,30 +747,35 @@ install(char *dir)
 			vadd(&compile, bprintf(&b, "-DGOARCH_%s", goos));
 		}	
 
-		bprintf(&b, "%s/%s", workdir, lastelem(files.p[i]));
+		if(!isgo && streq(gohostos, "darwin")) {
+			// To debug C programs on OS X, it is not enough to say -ggdb
+			// on the command line.  You have to leave the object files
+			// lying around too.  Leave them in pkg/obj/, which does not
+			// get removed when this tool exits.
+			bpathf(&b1, "%s/pkg/obj/%s", goroot, dir);
+			xmkdirall(bstr(&b1));
+			bpathf(&b, "%s/%s", bstr(&b1), lastelem(files.p[i]));
+		} else
+			bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
+
 		b.p[b.len-1] = 'o';  // was c or s
-		fixslash(&b);
 		vadd(&compile, "-o");
 		vadd(&compile, bstr(&b));
-		vadd(&link, bstr(&b));
-		vadd(&clean, bstr(&b));
-
 		vadd(&compile, files.p[i]);
+		bgrunv(bstr(&path), CheckExit, &compile);
 
-		runv(nil, bstr(&path), CheckExit, &compile);
-		vreset(&compile);
+		vadd(&link, bstr(&b));
+		vadd(&clean, bstr(&b));
 	}
+	bgwait();
 	
 	if(isgo) {
 		// The last loop was compiling individual files.
 		// Hand the Go files to the compiler en masse.
 		vreset(&compile);
-		bprintf(&b, "%s/bin/go-tool/%sg", goroot, gochar);
-		fixslash(&b);
-		vadd(&compile, bstr(&b));
+		vadd(&compile, bpathf(&b, "%s/bin/tool/%sg", goroot, gochar));
 
-		bprintf(&b, "%s/_go_.%s", workdir, gochar);
-		fixslash(&b);
+		bpathf(&b, "%s/_go_.%s", workdir, gochar);
 		vadd(&compile, "-o");
 		vadd(&compile, bstr(&b));
 		vadd(&clean, bstr(&b));
@@ -678,6 +806,16 @@ install(char *dir)
 
 	runv(nil, nil, CheckExit, &link);
 
+	// In package runtime, we install runtime.h and cgocall.h too,
+	// for use by cgo compilation.
+	if(streq(dir, "pkg/runtime")) {
+		copy(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch),
+			bpathf(&b1, "%s/src/pkg/runtime/cgocall.h", goroot));
+		copy(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch),
+			bpathf(&b1, "%s/src/pkg/runtime/runtime.h", goroot));
+	}
+
+
 out:
 	for(i=0; i<clean.len; i++)
 		xremove(clean.p[i]);
@@ -713,7 +851,7 @@ static bool
 shouldbuild(char *file, char *dir)
 {
 	char *name, *p;
-	int i, j, ret, true;
+	int i, j, ret;
 	Buf b;
 	Vec lines, fields;
 	
@@ -729,6 +867,13 @@ shouldbuild(char *file, char *dir)
 	// Omit test files.
 	if(contains(name, "_test"))
 		return 0;
+	
+	// cmd/go/doc.go has a giant /* */ comment before
+	// it gets to the important detail that it is not part of
+	// package main.  We don't parse those comments,
+	// so special case that file.
+	if(hassuffix(file, "cmd/go/doc.go") || hassuffix(file, "cmd\\go\\doc.go"))
+		return 0;
 
 	// Check file contents for // +build lines.
 	binit(&b);
@@ -777,47 +922,21 @@ out:
 	return ret;
 }
 
-// fixslash rewrites / to \ when the slash character is \, so that the paths look conventional.
-static void
-fixslash(Buf *b)
-{
-	int i;
-
-	if(slash[0] == '/')
-		return;
-	for(i=0; i<b->len; i++)
-		if(b->p[i] == '/')
-			b->p[i] = '\\';
-}
-
 // copy copies the file src to dst, via memory (so only good for small files).
 static void
 copy(char *dst, char *src)
 {
 	Buf b;
 	
+	if(vflag > 1)
+		xprintf("cp %s %s\n", src, dst);
+
 	binit(&b);
 	readfile(&b, src);
 	writefile(&b, dst);
 	bfree(&b);
 }
 
-/*
- * command implementations
- */
-
-// The env command prints the default environment.
-void
-cmdenv(int argc, char **argv)
-{
-	USED(argc);
-	USED(argv);
-	
-	xprintf("GOROOT=%s\n", goroot);
-	xprintf("GOARCH=%s\n", goarch);
-	xprintf("GOOS=%s\n", goos);
-}
-
 // buildorder records the order of builds for the 'go bootstrap' command.
 static char *buildorder[] = {
 	"lib9",
@@ -883,6 +1002,183 @@ static char *buildorder[] = {
 	"cmd/go",
 };
 
+// cleantab records the directories to clean in 'go clean'.
+// It is bigger than the buildorder because we clean all the
+// compilers but build only the $GOARCH ones.
+static char *cleantab[] = {
+	"cmd/5a",
+	"cmd/5c",
+	"cmd/5g",
+	"cmd/5l",
+	"cmd/6a",
+	"cmd/6c",
+	"cmd/6g",
+	"cmd/6l",
+	"cmd/8a",
+	"cmd/8c",
+	"cmd/8g",
+	"cmd/8l",
+	"cmd/cc",
+	"cmd/cov",
+	"cmd/gc",
+	"cmd/go",
+	"cmd/nm",
+	"cmd/pack",
+	"cmd/prof",
+	"lib9",
+	"libbio",
+	"libmach",
+	"pkg/bufio",
+	"pkg/bytes",
+	"pkg/container/heap",
+	"pkg/encoding/base64",
+	"pkg/encoding/gob",
+	"pkg/encoding/json",
+	"pkg/errors",
+	"pkg/flag",
+	"pkg/fmt",
+	"pkg/go/ast",
+	"pkg/go/build",
+	"pkg/go/parser",
+	"pkg/go/scanner",
+	"pkg/go/token",
+	"pkg/io",
+	"pkg/io/ioutil",
+	"pkg/log",
+	"pkg/math",
+	"pkg/net/url",
+	"pkg/os",
+	"pkg/os/exec",
+	"pkg/path",
+	"pkg/path/filepath",
+	"pkg/reflect",
+	"pkg/regexp",
+	"pkg/regexp/syntax",
+	"pkg/runtime",
+	"pkg/sort",
+	"pkg/strconv",
+	"pkg/strings",
+	"pkg/sync",
+	"pkg/sync/atomic",
+	"pkg/syscall",
+	"pkg/text/template",
+	"pkg/text/template/parse",
+	"pkg/time",
+	"pkg/unicode",
+	"pkg/unicode/utf16",
+	"pkg/unicode/utf8",
+};
+
+static void
+clean(void)
+{
+	int i, j, k;
+	Buf b, path;
+	Vec dir;
+	
+	binit(&b);
+	binit(&path);
+	vinit(&dir);
+	
+	for(i=0; i<nelem(cleantab); i++) {
+		bpathf(&path, "%s/src/%s", goroot, cleantab[i]);
+		xreaddir(&dir, bstr(&path));
+		// Remove generated files.
+		for(j=0; j<dir.len; j++) {
+			for(k=0; k<nelem(gentab); k++) {
+				if(hasprefix(dir.p[j], gentab[k].nameprefix))
+					xremove(bpathf(&b, "%s/%s", bstr(&path), dir.p[j]));
+			}
+		}
+		// Remove generated binary named for directory.
+		if(hasprefix(cleantab[i], "cmd/"))
+			xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
+	}
+
+	// Remove object tree.
+	xremoveall(bpathf(&b, "%s/pkg/obj", goroot));
+	
+	// Remove installed packages and tools.
+	xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
+	xremove(bpathf(&b, "%s/bin/tool", goroot));
+	
+	// Remove cached version info.
+	xremove(bpathf(&b, "%s/VERSION.cache", goroot));
+
+	bfree(&b);
+	bfree(&path);
+	vfree(&dir);
+}
+
+/*
+ * command implementations
+ */
+
+void
+usage(void)
+{
+	xprintf("usage: go tool dist [command]\n"
+		"Commands are:\n"
+		"\n"
+		"banner         print installation banner\n"
+		"bootstrap      rebuild everything\n"
+		"clean          deletes all built files\n"
+		"env [-p]       print environment (-p: include $PATH)\n"
+		"install [dir]  install individual directory\n"
+		"version        print Go version\n"
+		"\n"
+		"All commands take -v flags to emit extra information.\n"
+	);
+	xexit(2);
+}
+
+// The env command prints the default environment.
+void
+cmdenv(int argc, char **argv)
+{
+	bool pflag;
+	char *sep;
+	Buf b, b1;
+	char *format;
+
+	binit(&b);
+	binit(&b1);
+
+	format = "%s=\"%s\"";
+	pflag = 0;
+	ARGBEGIN{
+	case 'p':
+		pflag = 1;
+		break;
+	case 'v':
+		vflag++;
+		break;
+	case 'w':
+		format = "set %s=%s\n";
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 0)
+		usage();
+	
+	xprintf(format, "GOROOT", goroot);
+	xprintf(format, "GOARCH", goarch);
+	xprintf(format, "GOOS", goos);
+	if(pflag) {
+		sep = ":";
+		if(streq(gohostos, "windows"))
+			sep = ";";
+		xgetenv(&b, "PATH");
+		bprintf(&b1, "%s%s%s", gobin, sep, bstr(&b));
+		xprintf(format, "PATH", bstr(&b1));
+	}
+
+	bfree(&b);
+	bfree(&b1);
+}
+
 // The bootstrap command runs a build from scratch,
 // stopping at having installed the go_bootstrap command.
 void
@@ -892,25 +1188,160 @@ cmdbootstrap(int argc, char **argv)
 	Buf b;
 	char *p;
 
+	ARGBEGIN{
+	case 'v':
+		vflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 0)
+		usage();
+
+	clean();
 	setup();
 	
-	// TODO: nuke();
-	
 	binit(&b);
 	for(i=0; i<nelem(buildorder); i++) {
 		p = bprintf(&b, buildorder[i], gochar);
-		xprintf("%s\n", p);
+		if(vflag > 0)
+			xprintf("%s\n", p);
 		install(p);
 	}
 	bfree(&b);
 }
 
+static char*
+defaulttarg(void)
+{
+	char *p;
+	Buf pwd, src;
+	
+	binit(&pwd);
+	binit(&src);
+
+	xgetwd(&pwd);
+	p = btake(&pwd);
+	bpathf(&src, "%s/src/", goroot);
+	if(!hasprefix(p, bstr(&src)))
+		fatal("current directory %s is not under %s", p, bstr(&src));
+	p += src.len;
+
+	bfree(&pwd);
+	bfree(&src);
+	
+	return p;
+}
+
 // Install installs the list of packages named on the command line.
 void
 cmdinstall(int argc, char **argv)
 {
 	int i;
 
-	for(i=1; i<argc; i++)
+	ARGBEGIN{
+	case 'v':
+		vflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+	
+	if(argc == 0)
+		install(defaulttarg());
+
+	for(i=0; i<argc; i++)
 		install(argv[i]);
 }
+
+// Clean deletes temporary objects.
+// Clean -i deletes the installed objects too.
+void
+cmdclean(int argc, char **argv)
+{
+	ARGBEGIN{
+	case 'v':
+		vflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 0)
+		usage();
+
+	clean();
+}
+
+// Banner prints the 'now you've installed Go' banner.
+void
+cmdbanner(int argc, char **argv)
+{
+	char *pathsep;
+	Buf b, b1, search;
+
+	ARGBEGIN{
+	case 'v':
+		vflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 0)
+		usage();
+
+	binit(&b);
+	binit(&b1);
+	binit(&search);
+	
+	xprintf("\n");
+	xprintf("---\n");
+	xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
+	xprintf("Installed commands in %s\n", gobin);
+
+	// Check that gobin appears in $PATH.
+	xgetenv(&b, "PATH");
+	pathsep = ":";
+	if(streq(gohostos, "windows"))
+		pathsep = ";";
+	bprintf(&b1, "%s%s%s", pathsep, bstr(&b), pathsep);
+	bprintf(&search, "%s%s%s", pathsep, gobin, pathsep);
+	if(xstrstr(bstr(&b1), bstr(&search)) == nil)
+		xprintf("*** You need to add %s to your PATH.\n", gobin);
+
+	if(streq(gohostos, "darwin")) {
+		xprintf("\n"
+			"On OS X the debuggers must be installed setgrp procmod.\n"
+			"Read and run ./sudo.bash to install the debuggers.\n");
+	}
+	
+	if(!streq(goroot_final, goroot)) {
+		xprintf("\n"
+			"The binaries expect %s to be copied or moved to %s\n",
+			goroot, goroot_final);
+	}
+
+	bfree(&b);
+	bfree(&b1);
+	bfree(&search);
+}
+
+// Version prints the Go version.
+void
+cmdversion(int argc, char **argv)
+{
+	ARGBEGIN{
+	case 'v':
+		vflag++;
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 0)
+		usage();
+
+	xprintf("%s\n", findgoversion());
+}
diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c
new file mode 100644
index 0000000000000000000000000000000000000000..7cbff3fb87c1be324914138d9347fee3460987d7
--- /dev/null
+++ b/src/cmd/dist/buildruntime.c
@@ -0,0 +1,346 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "a.h"
+#include <stdio.h>
+
+/*
+ * Helpers for building pkg/runtime.
+ */
+
+// mkzversion writes zversion.go:
+//
+//	package runtime
+//	const defaultGoroot = <goroot>
+//	const theVersion = <version>
+//
+void
+mkzversion(char *dir, char *file)
+{
+	Buf b, out;
+	
+	binit(&b);
+	binit(&out);
+	
+	bwritestr(&out, bprintf(&b,
+		"// auto generated by go tool dist\n"
+		"\n"
+		"package runtime\n"
+		"\n"
+		"const defaultGoroot = `%s`\n"
+		"const theVersion = `%s`\n", goroot, goversion));
+
+	writefile(&out, file);
+	
+	bfree(&b);
+	bfree(&out);
+}
+
+// mkzgoarch writes zgoarch_$GOARCH.go:
+//
+//	package runtime
+//	const theGoarch = <goarch>
+//
+void
+mkzgoarch(char *dir, char *file)
+{
+	Buf b, out;
+	
+	binit(&b);
+	binit(&out);
+	
+	bwritestr(&out, bprintf(&b,
+		"// auto generated by go tool dist\n"
+		"\n"
+		"package runtime\n"
+		"\n"
+		"const theGoarch = `%s`\n", goarch));
+
+	writefile(&out, file);
+	
+	bfree(&b);
+	bfree(&out);
+}
+
+// mkzgoos writes zgoos_$GOOS.go:
+//
+//	package runtime
+//	const theGoos = <goos>
+//
+void
+mkzgoos(char *dir, char *file)
+{
+	Buf b, out;
+	
+	binit(&b);
+	binit(&out);
+	
+	bwritestr(&out, bprintf(&b,
+		"// auto generated by go tool dist\n"
+		"\n"
+		"package runtime\n"
+		"\n"
+		"const theGoos = `%s`\n", goos));
+
+	writefile(&out, file);
+	
+	bfree(&b);
+	bfree(&out);
+}
+
+static struct {
+	char *goarch;
+	char *goos;
+	char *hdr;
+} zasmhdr[] = {
+	{"386", "windows",
+		"#define	get_tls(r)	MOVL 0x14(FS), r\n"
+		"#define	g(r)	0(r)\n"
+		"#define	m(r)	4(r)\n"
+	},
+	{"386", "plan9",
+		"#define	get_tls(r)	MOVL _tos(SB), r \n"
+		"#define	g(r)	-8(r)\n"
+		"#define	m(r)	-4(r)\n"
+	},
+	{"386", "linux",
+		"// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n"
+		"// turn into %gs:-8 and %gs:-4 (using gcc syntax to denote\n"
+		"// what the machine sees as opposed to 8l input).\n"
+		"// 8l rewrites 0(GS) and 4(GS) into these.\n"
+		"//\n"
+		"// On Linux Xen, it is not allowed to use %gs:-8 and %gs:-4\n"
+		"// directly.  Instead, we have to store %gs:0 into a temporary\n"
+		"// register and then use -8(%reg) and -4(%reg).  This kind\n"
+		"// of addressing is correct even when not running Xen.\n"
+		"//\n"
+		"// 8l can rewrite MOVL 0(GS), CX into the appropriate pair\n"
+		"// of mov instructions, using CX as the intermediate register\n"
+		"// (safe because CX is about to be written to anyway).\n"
+		"// But 8l cannot handle other instructions, like storing into 0(GS),\n"
+		"// which is where these macros come into play.\n"
+		"// get_tls sets up the temporary and then g and r use it.\n"
+		"//\n"
+		"// The final wrinkle is that get_tls needs to read from %gs:0,\n"
+		"// but in 8l input it's called 8(GS), because 8l is going to\n"
+		"// subtract 8 from all the offsets, as described above.\n"
+		"#define	get_tls(r)	MOVL 8(GS), r\n"
+		"#define	g(r)	-8(r)\n"
+		"#define	m(r)	-4(r)\n"
+	},
+	{"386", "",
+		"#define	get_tls(r)\n"
+		"#define	g(r)	0(GS)\n"
+		"#define	m(r)	4(GS)\n"
+	},
+	
+	{"amd64", "windows",
+		"#define	get_tls(r) MOVQ 0x28(GS), r\n"
+		"#define	g(r) 0(r)\n"
+		"#define	m(r) 8(r)\n"
+	},
+	{"amd64", "",
+		"// The offsets 0 and 8 are known to:\n"
+		"//	../../cmd/6l/pass.c:/D_GS\n"
+		"//	cgo/gcc_linux_amd64.c:/^threadentry\n"
+		"//	cgo/gcc_darwin_amd64.c:/^threadentry\n"
+		"//\n"
+		"#define	get_tls(r)\n"
+		"#define	g(r) 0(GS)\n"
+		"#define	m(r) 8(GS)\n"
+	},
+	
+	{"arm", "",
+	"#define	g	R10\n"
+	"#define	m	R9\n"
+	"#define	LR	R14\n"
+	},
+};
+
+// mkzasm writes zasm_$GOOS_$GOARCH.h,
+// which contains struct offsets for use by
+// assembly files.  It also writes a copy to the work space
+// under the name zasm_GOOS_GOARCH.h (no expansion).
+// 
+void
+mkzasm(char *dir, char *file)
+{
+	int i, n;
+	char *aggr, *p;
+	Buf in, b, out;
+	Vec argv, lines, fields;
+
+	binit(&in);
+	binit(&b);
+	binit(&out);
+	vinit(&argv);
+	vinit(&lines);
+	vinit(&fields);
+	
+	bwritestr(&out, "// auto generated by go tool dist\n\n");
+	for(i=0; i<nelem(zasmhdr); i++) {
+		if(hasprefix(goarch, zasmhdr[i].goarch) && hasprefix(goos, zasmhdr[i].goos)) {
+			bwritestr(&out, zasmhdr[i].hdr);
+			goto ok;
+		}
+	}
+	fatal("unknown $GOOS/$GOARCH in mkzasm");
+ok:
+
+	// Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -a proc.c
+	// to get acid [sic] output.
+	vreset(&argv);
+	vadd(&argv, bpathf(&b, "%s/bin/tool/%sc", goroot, gochar));
+	vadd(&argv, bprintf(&b, "-DGOOS_%s", goos));
+	vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch));
+	vadd(&argv, bprintf(&b, "-I%s", workdir));
+	vadd(&argv, "-a");
+	vadd(&argv, "proc.c");
+	runv(&in, dir, CheckExit, &argv);
+	
+	// Convert input like
+	//	aggr G
+	//	{
+	//		Gobuf 24 sched;
+	//		'Y' 48 stack0;
+	//	}
+	// into output like
+	//	#define g_sched 24
+	//	#define g_stack0 48
+	//
+	aggr = nil;
+	splitlines(&lines, bstr(&in));
+	for(i=0; i<lines.len; i++) {
+		splitfields(&fields, lines.p[i]);
+		if(fields.len == 2 && streq(fields.p[0], "aggr")) {
+			if(streq(fields.p[1], "G"))
+				aggr = "g";
+			else if(streq(fields.p[1], "M"))
+				aggr = "m";
+			else if(streq(fields.p[1], "Gobuf"))
+				aggr = "gobuf";
+			else if(streq(fields.p[1], "WinCall"))
+				aggr = "wincall";
+		}
+		if(hasprefix(lines.p[i], "}"))
+			aggr = nil;
+		if(aggr && hasprefix(lines.p[i], "\t") && fields.len >= 2) {
+			n = fields.len;
+			p = fields.p[n-1];
+			if(p[xstrlen(p)-1] == ';')
+				p[xstrlen(p)-1] = '\0';
+			bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2]));
+		}
+	}
+	
+	// Write both to file and to workdir/zasm_GOOS_GOARCH.h.
+	writefile(&out, file);
+	writefile(&out, bprintf(&b, "%s/zasm_GOOS_GOARCH.h", workdir));
+
+	bfree(&in);
+	bfree(&b);
+	bfree(&out);
+	vfree(&argv);
+	vfree(&lines);
+	vfree(&fields);
+}
+
+static char *runtimedefs[] = {
+	"proc.c",
+	"iface.c",
+	"hashmap.c",
+	"chan.c",
+};
+
+// mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h,
+// which contains Go struct definitions equivalent to the C ones.
+// Mostly we just write the output of 6c -q to the file.
+// However, we run it on multiple files, so we have to delete
+// the duplicated definitions, and we don't care about the funcs
+// and consts, so we delete those too.
+// 
+void
+mkzruntimedefs(char *dir, char *file)
+{
+	int i, skip;
+	char *p;
+	Buf in, b, out;
+	Vec argv, lines, fields, seen;
+	
+	binit(&in);
+	binit(&b);
+	binit(&out);
+	vinit(&argv);
+	vinit(&lines);
+	vinit(&fields);
+	vinit(&seen);
+	
+	bwritestr(&out, "// auto generated by go tool dist\n"
+		"\n"
+		"package runtime\n"
+		"import \"unsafe\"\n"
+		"var _ unsafe.Pointer\n"
+		"\n"
+	);
+
+	
+	// Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -q
+	// on each of the runtimedefs C files.
+	vadd(&argv, bpathf(&b, "%s/bin/tool/%sc", goroot, gochar));
+	vadd(&argv, bprintf(&b, "-DGOOS_%s", goos));
+	vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch));
+	vadd(&argv, bprintf(&b, "-I%s", workdir));
+	vadd(&argv, "-q");
+	vadd(&argv, "");
+	p = argv.p[argv.len-1];
+	for(i=0; i<nelem(runtimedefs); i++) {
+		argv.p[argv.len-1] = runtimedefs[i];
+		runv(&b, dir, CheckExit, &argv);
+		bwriteb(&in, &b);
+	}
+	argv.p[argv.len-1] = p;
+		
+	// Process the aggregate output.
+	skip = 0;
+	splitlines(&lines, bstr(&in));
+	for(i=0; i<lines.len; i++) {
+		p = lines.p[i];
+		// Drop comment, func, and const lines.
+		if(hasprefix(p, "//") || hasprefix(p, "const") || hasprefix(p, "func"))
+			continue;
+		
+		// Note beginning of type or var decl, which can be multiline.
+		// Remove duplicates.  The linear check of seen here makes the
+		// whole processing quadratic in aggregate, but there are only
+		// about 100 declarations, so this is okay (and simple).
+		if(hasprefix(p, "type ") || hasprefix(p, "var ")) {
+			splitfields(&fields, p);
+			if(fields.len < 2)
+				continue;
+			if(find(fields.p[1], seen.p, seen.len) >= 0) {
+				if(streq(fields.p[fields.len-1], "{"))
+					skip = 1;  // skip until }
+				continue;
+			}
+			vadd(&seen, fields.p[1]);
+		}
+		if(skip) {
+			if(hasprefix(p, "}"))
+				skip = 0;
+			continue;
+		}
+		
+		bwritestr(&out, p);
+	}
+	
+	writefile(&out, file);
+
+	bfree(&in);
+	bfree(&b);
+	bfree(&out);
+	vfree(&argv);
+	vfree(&lines);
+	vfree(&fields);
+	vfree(&seen);
+}
diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c
new file mode 100644
index 0000000000000000000000000000000000000000..6829dedc78c6642bc35336d08bfbb99ddac08d24
--- /dev/null
+++ b/src/cmd/dist/goc2c.c
@@ -0,0 +1,727 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "a.h"
+
+/*
+ * Translate a .goc file into a .c file.  A .goc file is a combination
+ * of a limited form of Go with C.
+ */
+
+/*
+	package PACKAGENAME
+	{# line}
+	func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{
+	  C code with proper brace nesting
+	\}
+*/
+
+/*
+ * We generate C code which implements the function such that it can
+ * be called from Go and executes the C code.
+ */
+
+static char *input;
+static Buf *output;
+#define EOF -1
+
+static int
+xgetchar(void)
+{
+	int c;
+	
+	c = *input;
+	if(c == 0)
+		return EOF;
+	input++;
+	return c;
+}
+
+static void
+xungetc(void)
+{
+	input--;
+}
+
+static void
+xputchar(char c)
+{
+	bwrite(output, &c, 1);
+}
+
+static int
+xisspace(int c)
+{
+	return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+/* Whether we're emitting for gcc */
+static int gcc;
+
+/* File and line number */
+static const char *file;
+static unsigned int lineno = 1;
+
+/* List of names and types.  */
+struct params {
+	struct params *next;
+	char *name;
+	char *type;
+};
+
+/* index into type_table */
+enum {
+	Bool,
+	Float,
+	Int,
+	Uint,
+	Uintptr,
+	String,
+	Slice,
+	Eface,
+};
+
+static struct {
+	char *name;
+	int size;
+} type_table[] = {
+	/* variable sized first, for easy replacement */
+	/* order matches enum above */
+	/* default is 32-bit architecture sizes */
+	{"bool",		1},
+	{"float",	4},
+	{"int",		4},
+	{"uint",		4},
+	{"uintptr",	4},
+	{"String",	8},
+	{"Slice",	12},
+	{"Eface",	8},
+
+	/* fixed size */
+	{"float32",	4},
+	{"float64",	8},
+	{"byte",		1},
+	{"int8",		1},
+	{"uint8",	1},
+	{"int16",	2},
+	{"uint16",	2},
+	{"int32",	4},
+	{"uint32",	4},
+	{"int64",	8},
+	{"uint64",	8},
+
+	{nil},
+};
+
+/* Fixed structure alignment (non-gcc only) */
+int structround = 4;
+
+/* Unexpected EOF.  */
+static void
+bad_eof(void)
+{
+	fatal("%s:%ud: unexpected EOF\n", file, lineno);
+}
+
+/* Free a list of parameters.  */
+static void
+free_params(struct params *p)
+{
+	while (p != nil) {
+		struct params *next;
+
+		next = p->next;
+		xfree(p->name);
+		xfree(p->type);
+		xfree(p);
+		p = next;
+	}
+}
+
+/* Read a character, tracking lineno.  */
+static int
+getchar_update_lineno(void)
+{
+	int c;
+
+	c = xgetchar();
+	if (c == '\n')
+		++lineno;
+	return c;
+}
+
+/* Read a character, giving an error on EOF, tracking lineno.  */
+static int
+getchar_no_eof(void)
+{
+	int c;
+
+	c = getchar_update_lineno();
+	if (c == EOF)
+		bad_eof();
+	return c;
+}
+
+/* Read a character, skipping comments.  */
+static int
+getchar_skipping_comments(void)
+{
+	int c;
+
+	while (1) {
+		c = getchar_update_lineno();
+		if (c != '/')
+			return c;
+
+		c = xgetchar();
+		if (c == '/') {
+			do {
+				c = getchar_update_lineno();
+			} while (c != EOF && c != '\n');
+			return c;
+		} else if (c == '*') {
+			while (1) {
+				c = getchar_update_lineno();
+				if (c == EOF)
+					return EOF;
+				if (c == '*') {
+					do {
+						c = getchar_update_lineno();
+					} while (c == '*');
+					if (c == '/')
+						break;
+				}
+			}
+		} else {
+			xungetc();
+			return '/';
+		}
+	}
+}
+
+/*
+ * Read and return a token.  Tokens are string or character literals
+ * or else delimited by whitespace or by [(),{}].
+ * The latter are all returned as single characters.
+ */
+static char *
+read_token(void)
+{
+	int c, q;
+	char *buf;
+	unsigned int alc, off;
+	char* delims = "(),{}";
+
+	while (1) {
+		c = getchar_skipping_comments();
+		if (c == EOF)
+			return nil;
+		if (!xisspace(c))
+			break;
+	}
+	alc = 16;
+	buf = xmalloc(alc + 1);
+	off = 0;
+	if(c == '"' || c == '\'') {
+		q = c;
+		buf[off] = c;
+		++off;
+		while (1) {
+			if (off+2 >= alc) { // room for c and maybe next char
+				alc *= 2;
+				buf = xrealloc(buf, alc + 1);
+			}
+			c = getchar_no_eof();
+			buf[off] = c;
+			++off;
+			if(c == q)
+				break;
+			if(c == '\\') {
+				buf[off] = getchar_no_eof();
+				++off;
+			}
+		}
+	} else if (xstrrchr(delims, c) != nil) {
+		buf[off] = c;
+		++off;
+	} else {
+		while (1) {
+			if (off >= alc) {
+				alc *= 2;
+				buf = xrealloc(buf, alc + 1);
+			}
+			buf[off] = c;
+			++off;
+			c = getchar_skipping_comments();
+			if (c == EOF)
+				break;
+			if (xisspace(c) || xstrrchr(delims, c) != nil) {
+				if (c == '\n')
+					lineno--;
+				xungetc();
+				break;
+			}
+		}
+	}
+	buf[off] = '\0';
+	return buf;
+}
+
+/* Read a token, giving an error on EOF.  */
+static char *
+read_token_no_eof(void)
+{
+	char *token = read_token();
+	if (token == nil)
+		bad_eof();
+	return token;
+}
+
+/* Read the package clause, and return the package name.  */
+static char *
+read_package(void)
+{
+	char *token;
+
+	token = read_token_no_eof();
+	if (token == nil)
+		fatal("%s:%ud: no token\n", file, lineno);
+	if (!streq(token, "package")) {
+		fatal("%s:%ud: expected \"package\", got \"%s\"\n",
+			file, lineno, token);
+	}
+	return read_token_no_eof();
+}
+
+/* Read and copy preprocessor lines.  */
+static void
+read_preprocessor_lines(void)
+{
+	while (1) {
+		int c;
+
+		do {
+			c = getchar_skipping_comments();
+		} while (xisspace(c));
+		if (c != '#') {
+			xungetc();
+			break;
+		}
+		xputchar(c);
+		do {
+			c = getchar_update_lineno();
+			xputchar(c);
+		} while (c != '\n');
+	}
+}
+
+/*
+ * Read a type in Go syntax and return a type in C syntax.  We only
+ * permit basic types and pointers.
+ */
+static char *
+read_type(void)
+{
+	char *p, *op, *q;
+	int pointer_count;
+	unsigned int len;
+
+	p = read_token_no_eof();
+	if (*p != '*')
+		return p;
+	op = p;
+	pointer_count = 0;
+	while (*p == '*') {
+		++pointer_count;
+		++p;
+	}
+	len = xstrlen(p);
+	q = xmalloc(len + pointer_count + 1);
+	xmemmove(q, p, len);
+	while (pointer_count > 0) {
+		q[len] = '*';
+		++len;
+		--pointer_count;
+	}
+	q[len] = '\0';
+	xfree(op);
+	return q;
+}
+
+/* Return the size of the given type. */
+static int
+type_size(char *p)
+{
+	int i;
+
+	if(p[xstrlen(p)-1] == '*')
+		return type_table[Uintptr].size;
+
+	for(i=0; type_table[i].name; i++)
+		if(streq(type_table[i].name, p))
+			return type_table[i].size;
+	fatal("%s:%ud: unknown type %s\n", file, lineno, p);
+	return 0;
+}
+
+/*
+ * Read a list of parameters.  Each parameter is a name and a type.
+ * The list ends with a ')'.  We have already read the '('.
+ */
+static struct params *
+read_params(int *poffset)
+{
+	char *token;
+	struct params *ret, **pp, *p;
+	int offset, size, rnd;
+
+	ret = nil;
+	pp = &ret;
+	token = read_token_no_eof();
+	offset = 0;
+	if (!streq(token, ")")) {
+		while (1) {
+			p = xmalloc(sizeof(struct params));
+			p->name = token;
+			p->type = read_type();
+			p->next = nil;
+			*pp = p;
+			pp = &p->next;
+
+			size = type_size(p->type);
+			rnd = size;
+			if(rnd > structround)
+				rnd = structround;
+			if(offset%rnd)
+				offset += rnd - offset%rnd;
+			offset += size;
+
+			token = read_token_no_eof();
+			if (!streq(token, ","))
+				break;
+			token = read_token_no_eof();
+		}
+	}
+	if (!streq(token, ")")) {
+		fatal("%s:%ud: expected '('\n",
+			file, lineno);
+	}
+	if (poffset != nil)
+		*poffset = offset;
+	return ret;
+}
+
+/*
+ * Read a function header.  This reads up to and including the initial
+ * '{' character.  Returns 1 if it read a header, 0 at EOF.
+ */
+static int
+read_func_header(char **name, struct params **params, int *paramwid, struct params **rets)
+{
+	int lastline;
+	char *token;
+
+	lastline = -1;
+	while (1) {
+		token = read_token();
+		if (token == nil)
+			return 0;
+		if (streq(token, "func")) {
+			if(lastline != -1)
+				bwritef(output, "\n");
+			break;
+		}
+		if (lastline != lineno) {
+			if (lastline == lineno-1)
+				bwritef(output, "\n");
+			else
+				bwritef(output, "\n#line %d \"%s\"\n", lineno, file);
+			lastline = lineno;
+		}
+		bwritef(output, "%s ", token);
+	}
+
+	*name = read_token_no_eof();
+
+	token = read_token();
+	if (token == nil || !streq(token, "(")) {
+		fatal("%s:%ud: expected \"(\"\n",
+			file, lineno);
+	}
+	*params = read_params(paramwid);
+
+	token = read_token();
+	if (token == nil || !streq(token, "("))
+		*rets = nil;
+	else {
+		*rets = read_params(nil);
+		token = read_token();
+	}
+	if (token == nil || !streq(token, "{")) {
+		fatal("%s:%ud: expected \"{\"\n",
+			file, lineno);
+	}
+	return 1;
+}
+
+/* Write out parameters.  */
+static void
+write_params(struct params *params, int *first)
+{
+	struct params *p;
+
+	for (p = params; p != nil; p = p->next) {
+		if (*first)
+			*first = 0;
+		else
+			bwritef(output, ", ");
+		bwritef(output, "%s %s", p->type, p->name);
+	}
+}
+
+/* Write a 6g function header.  */
+static void
+write_6g_func_header(char *package, char *name, struct params *params,
+		     int paramwid, struct params *rets)
+{
+	int first, n;
+
+	bwritef(output, "void\n%s·%s(", package, name);
+	first = 1;
+	write_params(params, &first);
+
+	/* insert padding to align output struct */
+	if(rets != nil && paramwid%structround != 0) {
+		n = structround - paramwid%structround;
+		if(n & 1)
+			bwritef(output, ", uint8");
+		if(n & 2)
+			bwritef(output, ", uint16");
+		if(n & 4)
+			bwritef(output, ", uint32");
+	}
+
+	write_params(rets, &first);
+	bwritef(output, ")\n{\n");
+}
+
+/* Write a 6g function trailer.  */
+static void
+write_6g_func_trailer(struct params *rets)
+{
+	struct params *p;
+
+	for (p = rets; p != nil; p = p->next)
+		bwritef(output, "\tFLUSH(&%s);\n", p->name);
+	bwritef(output, "}\n");
+}
+
+/* Define the gcc function return type if necessary.  */
+static void
+define_gcc_return_type(char *package, char *name, struct params *rets)
+{
+	struct params *p;
+
+	if (rets == nil || rets->next == nil)
+		return;
+	bwritef(output, "struct %s_%s_ret {\n", package, name);
+	for (p = rets; p != nil; p = p->next)
+		bwritef(output, "  %s %s;\n", p->type, p->name);
+	bwritef(output, "};\n");
+}
+
+/* Write out the gcc function return type.  */
+static void
+write_gcc_return_type(char *package, char *name, struct params *rets)
+{
+	if (rets == nil)
+		bwritef(output, "void");
+	else if (rets->next == nil)
+		bwritef(output, "%s", rets->type);
+	else
+		bwritef(output, "struct %s_%s_ret", package, name);
+}
+
+/* Write out a gcc function header.  */
+static void
+write_gcc_func_header(char *package, char *name, struct params *params,
+		      struct params *rets)
+{
+	int first;
+	struct params *p;
+
+	define_gcc_return_type(package, name, rets);
+	write_gcc_return_type(package, name, rets);
+	bwritef(output, " %s_%s(", package, name);
+	first = 1;
+	write_params(params, &first);
+	bwritef(output, ") asm (\"%s.%s\");\n", package, name);
+	write_gcc_return_type(package, name, rets);
+	bwritef(output, " %s_%s(", package, name);
+	first = 1;
+	write_params(params, &first);
+	bwritef(output, ")\n{\n");
+	for (p = rets; p != nil; p = p->next)
+		bwritef(output, "  %s %s;\n", p->type, p->name);
+}
+
+/* Write out a gcc function trailer.  */
+static void
+write_gcc_func_trailer(char *package, char *name, struct params *rets)
+{
+	if (rets == nil)
+		;
+	else if (rets->next == nil)
+		bwritef(output, "return %s;\n", rets->name);
+	else {
+		struct params *p;
+
+		bwritef(output, "  {\n    struct %s_%s_ret __ret;\n", package, name);
+		for (p = rets; p != nil; p = p->next)
+			bwritef(output, "    __ret.%s = %s;\n", p->name, p->name);
+		bwritef(output, "    return __ret;\n  }\n");
+	}
+	bwritef(output, "}\n");
+}
+
+/* Write out a function header.  */
+static void
+write_func_header(char *package, char *name,
+		  struct params *params, int paramwid,
+		  struct params *rets)
+{
+	if (gcc)
+		write_gcc_func_header(package, name, params, rets);
+	else
+		write_6g_func_header(package, name, params, paramwid, rets);
+	bwritef(output, "#line %d \"%s\"\n", lineno, file);
+}
+
+/* Write out a function trailer.  */
+static void
+write_func_trailer(char *package, char *name,
+		   struct params *rets)
+{
+	if (gcc)
+		write_gcc_func_trailer(package, name, rets);
+	else
+		write_6g_func_trailer(rets);
+}
+
+/*
+ * Read and write the body of the function, ending in an unnested }
+ * (which is read but not written).
+ */
+static void
+copy_body(void)
+{
+	int nesting = 0;
+	while (1) {
+		int c;
+
+		c = getchar_no_eof();
+		if (c == '}' && nesting == 0)
+			return;
+		xputchar(c);
+		switch (c) {
+		default:
+			break;
+		case '{':
+			++nesting;
+			break;
+		case '}':
+			--nesting;
+			break;
+		case '/':
+			c = getchar_update_lineno();
+			xputchar(c);
+			if (c == '/') {
+				do {
+					c = getchar_no_eof();
+					xputchar(c);
+				} while (c != '\n');
+			} else if (c == '*') {
+				while (1) {
+					c = getchar_no_eof();
+					xputchar(c);
+					if (c == '*') {
+						do {
+							c = getchar_no_eof();
+							xputchar(c);
+						} while (c == '*');
+						if (c == '/')
+							break;
+					}
+				}
+			}
+			break;
+		case '"':
+		case '\'':
+			{
+				int delim = c;
+				do {
+					c = getchar_no_eof();
+					xputchar(c);
+					if (c == '\\') {
+						c = getchar_no_eof();
+						xputchar(c);
+						c = '\0';
+					}
+				} while (c != delim);
+			}
+			break;
+		}
+	}
+}
+
+/* Process the entire file.  */
+static void
+process_file(void)
+{
+	char *package, *name;
+	struct params *params, *rets;
+	int paramwid;
+
+	package = read_package();
+	read_preprocessor_lines();
+	while (read_func_header(&name, &params, &paramwid, &rets)) {
+		write_func_header(package, name, params, paramwid, rets);
+		copy_body();
+		write_func_trailer(package, name, rets);
+		xfree(name);
+		free_params(params);
+		free_params(rets);
+	}
+	xfree(package);
+}
+
+void
+goc2c(char *goc, char *c)
+{
+	Buf in, out;
+	
+	binit(&in);
+	binit(&out);
+	
+	file = goc;
+	readfile(&in, goc);
+
+	// TODO: set gcc=1 when using gcc
+
+	if(!gcc && streq(goarch, "amd64")) {
+		type_table[Uintptr].size = 8;
+		type_table[String].size = 16;
+		type_table[Slice].size = 8+4+4;
+		type_table[Eface].size = 8+8;
+		structround = 8;
+	}
+
+	bprintf(&out, "// auto generated by go tool dist\n\n");
+	input = bstr(&in);
+	output = &out;
+
+	process_file();
+	
+	writefile(&out, c);
+}
diff --git a/src/cmd/dist/main.c b/src/cmd/dist/main.c
index adfdd2d4a6d9b587e388134db1aa25b05af305b2..72a7579d14230f132e9480678153733e9a6847f9 100644
--- a/src/cmd/dist/main.c
+++ b/src/cmd/dist/main.c
@@ -4,14 +4,20 @@
 
 #include "a.h"
 
+int vflag;
+char *argv0;
+
 // cmdtab records the available commands.
 static struct {
 	char *name;
 	void (*f)(int, char**);
 } cmdtab[] = {
+	{"banner", cmdbanner},
 	{"bootstrap", cmdbootstrap},
+	{"clean", cmdclean},
 	{"env", cmdenv},
 	{"install", cmdinstall},
+	{"version", cmdversion},
 };
 
 // The OS-specific main calls into the portable code here.
@@ -20,12 +26,8 @@ xmain(int argc, char **argv)
 {
 	int i;
 
-	if(argc <= 1) {
-		xprintf("go tool dist commands:\n");
-		for(i=0; i<nelem(cmdtab); i++)
-			xprintf("\t%s\n", cmdtab[i].name);
-		xexit(1);
-	}
+	if(argc <= 1)
+		usage();
 	
 	for(i=0; i<nelem(cmdtab); i++) {
 		if(streq(cmdtab[i].name, argv[1])) {
@@ -34,5 +36,6 @@ xmain(int argc, char **argv)
 		}
 	}
 
-	fatal("unknown command %s", argv[1]);
+	xprintf("unknown command %s\n", argv[1]);
+	usage();
 }
diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c
index 92d644876d8cc6c4cd37f44c4573b91e486dff6f..465a86c0df82d4d128d8db665d7209a584d38500 100644
--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -40,6 +40,36 @@ bprintf(Buf *b, char *fmt, ...)
 	return bstr(b);
 }
 
+// bpathf is the same as bprintf (on windows it turns / into \ after the printf).
+// It returns a pointer to the NUL-terminated buffer contents.
+char*
+bpathf(Buf *b, char *fmt, ...)
+{
+	va_list arg;
+	char buf[4096];
+	
+	breset(b);
+	va_start(arg, fmt);
+	vsnprintf(buf, sizeof buf, fmt, arg);
+	va_end(arg);
+	bwritestr(b, buf);
+	return bstr(b);
+}
+
+// bwritef is like bprintf but does not reset the buffer
+// and does not return the NUL-terminated string.
+void
+bwritef(Buf *b, char *fmt, ...)
+{
+	va_list arg;
+	char buf[4096];
+	
+	va_start(arg, fmt);
+	vsnprintf(buf, sizeof buf, fmt, arg);
+	va_end(arg);
+	bwritestr(b, buf);
+}
+
 // breadfrom appends to b all the data that can be read from fd.
 static void
 breadfrom(Buf *b, int fd)
@@ -69,6 +99,8 @@ xgetenv(Buf *b, char *name)
 		bwritestr(b, p);
 }
 
+static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg);
+
 // run runs the command named by cmd.
 // If b is not nil, run replaces b with the output of the command.
 // If dir is not nil, run runs the command in that directory.
@@ -92,15 +124,43 @@ run(Buf *b, char *dir, int mode, char *cmd, ...)
 	vfree(&argv);
 }
 
-
 // runv is like run but takes a vector.
 void
 runv(Buf *b, char *dir, int mode, Vec *argv)
 {
-	int i, p[2], pid, status;
+	genrun(b, dir, mode, argv, 1);
+}
+
+// bgrunv is like run but runs the command in the background.
+// bgwait waits for pending bgrunv to finish.
+void
+bgrunv(char *dir, int mode, Vec *argv)
+{
+	genrun(nil, dir, mode, argv, 0);
+}
+
+#define MAXBG 4 /* maximum number of jobs to run at once */
+
+static struct {
+	int pid;
+	int mode;
+	char *cmd;
+} bg[MAXBG];
+static int nbg;
+
+static void bgwait1(void);
+
+// genrun is the generic run implementation.
+static void
+genrun(Buf *b, char *dir, int mode, Vec *argv, int wait)
+{
+	int i, p[2], pid;
 	Buf cmd;
 	char *q;
 
+	while(nbg >= nelem(bg))
+		bgwait1();
+
 	// Generate a copy of the command to show in a log.
 	// Substitute $WORK for the work directory.
 	binit(&cmd);
@@ -114,8 +174,8 @@ runv(Buf *b, char *dir, int mode, Vec *argv)
 		}
 		bwritestr(&cmd, q);
 	}
-	printf("%s\n", bstr(&cmd));
-	bfree(&cmd);
+	if(vflag > 1)
+		xprintf("%s\n", bstr(&cmd));
 
 	if(b != nil) {
 		breset(b);
@@ -143,6 +203,7 @@ runv(Buf *b, char *dir, int mode, Vec *argv)
 		}
 		vadd(argv, nil);
 		execvp(argv->p[0], argv->p);
+		fprintf(stderr, "%s\n", bstr(&cmd));
 		fprintf(stderr, "exec %s: %s\n", argv->p[0], strerror(errno));
 		_exit(1);
 	}
@@ -151,18 +212,55 @@ runv(Buf *b, char *dir, int mode, Vec *argv)
 		breadfrom(b, p[0]);
 		close(p[0]);
 	}
-wait:
+
+	if(nbg < 0)
+		fatal("bad bookkeeping");
+	bg[nbg].pid = pid;
+	bg[nbg].mode = mode;
+	bg[nbg].cmd = btake(&cmd);
+	nbg++;
+	
+	if(wait)
+		bgwait();
+
+	bfree(&cmd);
+}
+
+// bgwait1 waits for a single background job.
+static void
+bgwait1(void)
+{
+	int i, pid, status, mode;
+	char *cmd;
+
 	errno = 0;
-	if(waitpid(pid, &status, 0) != pid) {
-		if(errno == EINTR)
-			goto wait;
-		fatal("waitpid: %s", strerror(errno));
+	while((pid = wait(&status)) < 0) {
+		if(errno != EINTR)
+			fatal("waitpid: %s", strerror(errno));
 	}
-	if(mode==CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
-		if(b != nil)
-			fwrite(b->p, b->len, 1, stderr);
-		fatal("%s failed", argv->p[0]);
+	for(i=0; i<nbg; i++)
+		if(bg[i].pid == pid)
+			goto ok;
+	fatal("waitpid: unexpected pid");
+
+ok:
+	cmd = bg[i].cmd;
+	mode = bg[i].mode;
+	bg[i].pid = 0;
+	bg[i] = bg[--nbg];
+	
+	if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
+		fatal("FAILED: %s", cmd);
 	}
+	xfree(cmd);
+}
+
+// bgwait waits for all the background jobs.
+void
+bgwait(void)
+{
+	while(nbg > 0)
+		bgwait1();
 }
 
 // xgetwd replaces b with the current directory.
@@ -288,6 +386,8 @@ xmkdirall(char *p)
 void
 xremove(char *p)
 {
+	if(vflag > 1)
+		xprintf("rm %s\n", p);
 	unlink(p);
 }
 
@@ -308,8 +408,12 @@ xremoveall(char *p)
 			bprintf(&b, "%s/%s", p, dir.p[i]);
 			xremoveall(bstr(&b));
 		}
+		if(vflag > 1)
+			xprintf("rm %s\n", p);
 		rmdir(p);
 	} else {
+		if(vflag > 1)
+			xprintf("rm %s\n", p);
 		unlink(p);
 	}
 	
@@ -526,9 +630,9 @@ main(int argc, char **argv)
 
 	binit(&b);
 	p = argv[0];
-	if(hassuffix(p, "bin/go-tool/dist")) {
+	if(hassuffix(p, "bin/tool/dist")) {
 		default_goroot = xstrdup(p);
-		default_goroot[strlen(p)-strlen("bin/go-tool/dist")] = '\0';
+		default_goroot[strlen(p)-strlen("bin/tool/dist")] = '\0';
 	}
 	
 	slash = "/";