diff --git a/src/cmd/dist/README b/src/cmd/dist/README
index e6d08cf0284be3c75c9856755e66bf8fe1233be8..0649e887f45a4e162bef8e8279cc4b1ffaba5b5e 100644
--- a/src/cmd/dist/README
+++ b/src/cmd/dist/README
@@ -1,45 +1,27 @@
 This program, dist, is the bootstrapping tool for the Go distribution.
-It takes care of building the C programs (like the Go compiler) and
-the initial bootstrap copy of the go tool.  It also serves as a catch-all
-to replace odd jobs previously done with shell scripts.
 
-Dist is itself written in very simple C.  All interaction with C libraries,
-even standard C libraries, is confined to a single system-specific file
-(plan9.c, unix.c, windows.c), to aid portability.  Functionality needed
-by other files should be exposed via the portability layer.  Functions
-in the portability layer begin with an x prefix when they would otherwise
-use the same name as or be confused for an existing function.
-For example, xprintf is the portable printf.
+As of Go 1.5, dist and other parts of the compiler toolchain are written
+in Go, making bootstrapping a little more involved than in the past.
+The approach is to build the current release of Go with an earlier one.
 
-By far the most common data types in dist are strings and arrays of
-strings.  Instead of using char* and char**, though, dist uses two named
-data structures, Buf and Vec, which own all the data they point at.
-The Buf operations are functions beginning with b; the Vec operations
-are functions beginning with v.  The basic form of any function declaring
-Bufs or Vecs on the stack should be
+The process to install Go 1.x, for x ≥ 5, is:
 
-	void
-	myfunc(void)
-	{
-		Buf b1, b2;
-		Vec v1;
-		
-		binit(&b1);
-		binit(&b2);
-		vinit(&v1);
-		
-		... main code ...
-		bprintf(&b1, "hello, world");
-		vadd(&v1, bstr(&b1));  // v1 takes a copy of its argument
-		bprintf(&b2, "another string");
-		vadd(&v1, bstr(&b2));  // v1 now has two strings
-		
-		bfree(&b1);
-		bfree(&b2);
-		vfree(&v1);
-	}
-	
-The binit/vinit calls prepare a buffer or vector for use, initializing the 
-data structures, and the bfree/vfree calls free any memory they are still
-holding onto.  Use of this idiom gives us lexically scoped allocations.
+1. Build cmd/dist with Go 1.4.
+2. Using dist, build Go 1.x compiler toolchain with Go 1.4.
+3. Using dist, rebuild Go 1.x compiler toolchain with itself.
+4. Using dist, build Go 1.x cmd/go (as go_bootstrap) with Go 1.x compiler toolchain.
+5. Using go_bootstrap, build the remaining Go 1.x standard library and commands.
 
+NOTE: During the transition from the old C-based toolchain to the Go-based one,
+step 2 also builds the parts of the toolchain written in C, and step 3 does not
+recompile those.
+
+Because of backward compatibility, although the steps above say Go 1.4,
+in practice any release ≥ Go 1.4 but < Go 1.x will work as the bootstrap base.
+
+See golang.org/s/go15bootstrap for more details.
+
+Compared to Go 1.4 and earlier, dist will also take over much of what used to
+be done by make.bash/make.bat/make.rc and all of what used to be done by
+run.bash/run.bat/run.rc, because it is nicer to implement that logic in Go
+than in three different scripting languages simultaneously.
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index e4b8b58e4373acb7de43a9f6fa6353efbbfe6091..9e4d1e3c22aef85b803a5baeb0deb6451369ebaa 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -2,47 +2,58 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
-#include "arg.h"
+package main
 
-/*
- * Initialization for any invocation.
- */
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+// Initialization for any invocation.
 
 // The usual variables.
-char *goarch;
-char *gobin;
-char *gohostarch;
-char *gohostchar;
-char *gohostos;
-char *goos;
-char *goarm;
-char *go386;
-char *goroot = GOROOT_FINAL;
-char *goroot_final = GOROOT_FINAL;
-char *goextlinkenabled = "";
-char *workdir;
-char *tooldir;
-char *gochar;
-char *goversion;
-char *slash;	// / for unix, \ for windows
-char *defaultcc;
-char *defaultcflags;
-char *defaultldflags;
-char *defaultcxxtarget;
-char *defaultcctarget;
-bool	rebuildall;
-bool defaultclang;
-
-static bool shouldbuild(char*, char*);
-static void dopack(char*, char*, char**, int);
-static char *findgoversion(void);
+var (
+	goarch           string
+	gobin            string
+	gohostarch       string
+	gohostchar       string
+	gohostos         string
+	goos             string
+	goarm            string
+	go386            string
+	goroot           string
+	goroot_final     string
+	goextlinkenabled string
+	workdir          string
+	tooldir          string
+	gochar           string
+	goversion        string
+	oldgoos          string
+	oldgoarch        string
+	oldgochar        string
+	slash            string
+	defaultcc        string
+	defaultcflags    string
+	defaultldflags   string
+	defaultcxxtarget string
+	defaultcctarget  string
+	rebuildall       bool
+	defaultclang     bool
+
+	sflag bool // build static binaries
+	vflag int  // verbosity
+)
 
 // The known architecture letters.
-static char *gochars = "566899";
+var gochars = "566899"
 
 // The known architectures.
-static char *okgoarch[] = {
+var okgoarch = []string{
 	// same order as gochars
 	"arm",
 	"amd64",
@@ -50,10 +61,10 @@ static char *okgoarch[] = {
 	"386",
 	"ppc64",
 	"ppc64le",
-};
+}
 
 // The known operating systems.
-static char *okgoos[] = {
+var okgoos = []string{
 	"darwin",
 	"dragonfly",
 	"linux",
@@ -65,294 +76,252 @@ static char *okgoos[] = {
 	"openbsd",
 	"plan9",
 	"windows",
-};
-
-static void rmworkdir(void);
+}
 
 // find reports the first index of p in l[0:n], or else -1.
-int
-find(char *p, char **l, int n)
-{
-	int i;
-
-	for(i=0; i<n; i++)
-		if(streq(p, l[i]))
-			return i;
-	return -1;
+func find(p string, l []string) int {
+	for i, s := range l {
+		if p == s {
+			return i
+		}
+	}
+	return -1
 }
 
-// init handles initialization of the various global state, like goroot and goarch.
-void
-init(void)
-{
-	char *p;
-	int i;
-	Buf b;
-
-	binit(&b);
-
-	xgetenv(&b, "GOROOT");
-	if(b.len > 0) {
-		// if not "/", then strip trailing path separator
-		if(b.len >= 2 && b.p[b.len - 1] == slash[0])
-			b.len--;
-		goroot = btake(&b);
-	}
-
-	xgetenv(&b, "GOBIN");
-	if(b.len == 0)
-		bprintf(&b, "%s%sbin", goroot, slash);
-	gobin = btake(&b);
-
-	xgetenv(&b, "GOOS");
-	if(b.len == 0)
-		bwritestr(&b, gohostos);
-	goos = btake(&b);
-	if(find(goos, okgoos, nelem(okgoos)) < 0)
-		fatal("unknown $GOOS %s", goos);
-
-	xgetenv(&b, "GOARM");
-	if(b.len == 0)
-		bwritestr(&b, xgetgoarm());
-	goarm = btake(&b);
-
-	xgetenv(&b, "GO386");
-	if(b.len == 0) {
-		if(cansse2())
-			bwritestr(&b, "sse2");
-		else
-			bwritestr(&b, "387");
-	}
-	go386 = btake(&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);
-
-	i = find(gohostarch, okgoarch, nelem(okgoarch));
-	if(i < 0)
-		fatal("unknown $GOHOSTARCH %s", gohostarch);
-	bprintf(&b, "%c", gochars[i]);
-	gohostchar = btake(&b);
-
-	xgetenv(&b, "GOARCH");
-	if(b.len == 0)
-		bwritestr(&b, gohostarch);
-	goarch = btake(&b);
-	i = find(goarch, okgoarch, nelem(okgoarch));
-	if(i < 0)
-		fatal("unknown $GOARCH %s", goarch);
-	bprintf(&b, "%c", gochars[i]);
-	gochar = btake(&b);
-
-	xgetenv(&b, "GO_EXTLINK_ENABLED");
-	if(b.len > 0) {
-		goextlinkenabled = btake(&b);
-		if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1"))
-			fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled);
-	}
-	
-	xgetenv(&b, "CC");
-	if(b.len == 0) {
+// xinit handles initialization of the various global state, like goroot and goarch.
+func xinit() {
+	goroot = os.Getenv("GOROOT")
+	if slash == "/" && len(goroot) > 1 || slash == `\` && len(goroot) > 3 {
+		// if not "/" or "c:\", then strip trailing path separator
+		goroot = strings.TrimSuffix(goroot, slash)
+	}
+	if goroot == "" {
+		fatal("$GOROOT must be set")
+	}
+
+	goroot_final = os.Getenv("GOROOT_FINAL")
+	if goroot_final == "" {
+		goroot_final = goroot
+	}
+
+	b := os.Getenv("GOBIN")
+	if b == "" {
+		b = goroot + slash + "bin"
+	}
+	gobin = b
+
+	b = os.Getenv("GOOS")
+	if b == "" {
+		b = gohostos
+	}
+	goos = b
+	if find(goos, okgoos) < 0 {
+		fatal("unknown $GOOS %s", goos)
+	}
+
+	b = os.Getenv("GOARM")
+	if b == "" {
+		b = xgetgoarm()
+	}
+	goarm = b
+
+	b = os.Getenv("GO386")
+	if b == "" {
+		if cansse2() {
+			b = "sse2"
+		} else {
+			b = "387"
+		}
+	}
+	go386 = b
+
+	p := pathf("%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)
+	}
+
+	b = os.Getenv("GOHOSTARCH")
+	if b != "" {
+		gohostarch = b
+	}
+
+	i := find(gohostarch, okgoarch)
+	if i < 0 {
+		fatal("unknown $GOHOSTARCH %s", gohostarch)
+	}
+	gohostchar = gochars[i : i+1]
+
+	b = os.Getenv("GOARCH")
+	if b == "" {
+		b = gohostarch
+	}
+	goarch = b
+	i = find(goarch, okgoarch)
+	if i < 0 {
+		fatal("unknown $GOARCH %s", goarch)
+	}
+	gochar = gochars[i : i+1]
+
+	b = os.Getenv("GO_EXTLINK_ENABLED")
+	if b != "" {
+		if b != "0" && b != "1" {
+			fatal("unknown $GO_EXTLINK_ENABLED %s", b)
+		}
+		goextlinkenabled = b
+	}
+
+	b = os.Getenv("CC")
+	if b == "" {
 		// Use clang on OS X, because gcc is deprecated there.
 		// Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that
 		// actually runs clang. We prepare different command
 		// lines for the two binaries, so it matters what we call it.
 		// See golang.org/issue/5822.
-		if(defaultclang)
-			bprintf(&b, "clang");
-		else
-			bprintf(&b, "gcc");
+		if defaultclang {
+			b = "clang"
+		} else {
+			b = "gcc"
+		}
 	}
-	defaultcc = btake(&b);
+	defaultcc = b
 
-	xgetenv(&b, "CFLAGS");
-	defaultcflags = btake(&b);
+	defaultcflags = os.Getenv("CFLAGS")
 
-	xgetenv(&b, "LDFLAGS");
-	defaultldflags = btake(&b);
+	defaultldflags = os.Getenv("LDFLAGS")
 
-	xgetenv(&b, "CC_FOR_TARGET");
-	if(b.len == 0) {
-		bprintf(&b, defaultcc);
+	b = os.Getenv("CC_FOR_TARGET")
+	if b == "" {
+		b = defaultcc
 	}
-	defaultcctarget = btake(&b);
-
-	xgetenv(&b, "CXX_FOR_TARGET");
-	if(b.len == 0) {
-		xgetenv(&b, "CXX");
-		if(b.len == 0) {
-			if(defaultclang)
-				bprintf(&b, "clang++");
-			else
-				bprintf(&b, "g++");
+	defaultcctarget = b
+
+	b = os.Getenv("CXX_FOR_TARGET")
+	if b == "" {
+		b = os.Getenv("CXX")
+		if b == "" {
+			if defaultclang {
+				b = "clang++"
+			} else {
+				b = "g++"
+			}
 		}
 	}
-	defaultcxxtarget = btake(&b);
-
-	xsetenv("GOROOT", goroot);
-	xsetenv("GOARCH", goarch);
-	xsetenv("GOOS", goos);
-	xsetenv("GOARM", goarm);
-	xsetenv("GO386", go386);
+	defaultcxxtarget = b
+
+	// For tools being invoked but also for os.ExpandEnv.
+	os.Setenv("GO386", go386)
+	os.Setenv("GOARCH", goarch)
+	os.Setenv("GOARM", goarm)
+	os.Setenv("GOHOSTARCH", gohostarch)
+	os.Setenv("GOHOSTOS", gohostos)
+	os.Setenv("GOOS", goos)
+	os.Setenv("GOROOT", goroot)
+	os.Setenv("GOROOT_FINAL", goroot_final)
 
 	// Make the environment more predictable.
-	xsetenv("LANG", "C");
-	xsetenv("LANGUAGE", "en_US.UTF8");
-
-	goversion = findgoversion();
+	os.Setenv("LANG", "C")
+	os.Setenv("LANGUAGE", "en_US.UTF8")
 
-	workdir = xworkdir();
-	xatexit(rmworkdir);
+	goversion = findgoversion()
 
-	bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch);
-	tooldir = btake(&b);
+	workdir = xworkdir()
+	xatexit(rmworkdir)
 
-	bfree(&b);
+	tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
 }
 
 // rmworkdir deletes the work directory.
-static void
-rmworkdir(void)
-{
-	if(vflag > 1)
-		errprintf("rm -rf %s\n", workdir);
-	xremoveall(workdir);
+func rmworkdir() {
+	if vflag > 1 {
+		errprintf("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--;
+func chomp(s string) string {
+	return strings.TrimRight(s, " \t\r\n")
 }
 
-static char*
-branchtag(char *branch, bool *precise)
-{
-	char *tag, *p, *q;
-	int i;
-	Buf b, arg;
-	Vec tags;
-
-	binit(&b);
-	binit(&arg);
-	vinit(&tags);
-
-	bprintf(&arg, "master..%s", branch);
-	run(&b, goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", bstr(&arg), nil);
-
-	splitlines(&tags, bstr(&b));
-	tag = branch;
-	for(i=0; i < tags.len; i++) {
+func branchtag(branch string) (tag string, precise bool) {
+	b := run(goroot, CheckExit, "git", "log", "--decorate=full", "--format=format:%d", "master.."+branch)
+	tag = branch
+	for _, line := range splitlines(b) {
 		// Each line is either blank, or looks like
 		//	  (tag: refs/tags/go1.4rc2, refs/remotes/origin/release-branch.go1.4, refs/heads/release-branch.go1.4)
 		// We need to find an element starting with refs/tags/.
-		p = xstrstr(tags.p[i], " refs/tags/");
-		if(p == nil)
-			continue;
-		p += xstrlen(" refs/tags/");
+		i := strings.Index(line, " refs/tags/")
+		if i < 0 {
+			continue
+		}
+		i += len(" refs/tags/")
 		// The tag name ends at a comma or paren (prefer the first).
-		q = xstrstr(p, ",");
-		if(q == nil)
-			q = xstrstr(p, ")");
-		if(q == nil)
-			continue;  // malformed line; ignore it
-		*q = '\0';
-		tag = xstrdup(p);
-		if(i == 0)
-			*precise = 1;  // tag denotes HEAD
-		break;
-	}
-
-	bfree(&b);
-	bfree(&arg);
-	vfree(&tags);
-	return tag;
+		j := strings.Index(line[i:], ",")
+		if j < 0 {
+			j = strings.Index(line[i:], ")")
+		}
+		if j < 0 {
+			continue // malformed line; ignore it
+		}
+		tag = line[i : i+j]
+		if i == 0 {
+			precise = true // tag denotes HEAD
+		}
+		break
+	}
+	return
 }
 
 // findgoversion determines the Go version to use in the version string.
-static char*
-findgoversion(void)
-{
-	char *tag, *p;
-	bool precise;
-	Buf b, path, bmore, branch;
-
-	binit(&b);
-	binit(&path);
-	binit(&bmore);
-	binit(&branch);
-
+func findgoversion() string {
 	// The $GOROOT/VERSION file takes priority, for distributions
 	// without the source repo.
-	bpathf(&path, "%s/VERSION", goroot);
-	if(isfile(bstr(&path))) {
-		readfile(&b, bstr(&path));
-		chomp(&b);
+	path := pathf("%s/VERSION", goroot)
+	if isfile(path) {
+		b := chomp(readfile(path))
 		// Commands such as "dist version > VERSION" will cause
 		// the shell to create an empty VERSION file and set dist's
 		// stdout to its fd. dist in turn looks at VERSION and uses
 		// its content if available, which is empty at this point.
-		if(b.len > 0)
-			goto done;
+		// Only use the VERSION file if it is non-empty.
+		if b != "" {
+			return b
+		}
 	}
 
 	// The $GOROOT/VERSION.cache file is a cache to avoid invoking
 	// git 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;
+	path = pathf("%s/VERSION.cache", goroot)
+	if isfile(path) {
+		return chomp(readfile(path))
 	}
 
 	// Otherwise, use Git.
 	// What is the current branch?
-	run(&branch, goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD", nil);
-	chomp(&branch);
+	branch := chomp(run(goroot, CheckExit, "git", "rev-parse", "--abbrev-ref", "HEAD"))
 
 	// What are the tags along the current branch?
-	tag = "devel";
-	precise = 0;
+	tag := "devel"
+	precise := false
 
 	// If we're on a release branch, use the closest matching tag
 	// that is on the release branch (and not on the master branch).
-	if(hasprefix(bstr(&branch), "release-branch."))
-		tag = branchtag(bstr(&branch), &precise);
+	if strings.HasPrefix(branch, "release-branch.") {
+		tag, precise = branchtag(branch)
+	}
 
-	bprintf(&b, "%s", tag);
-	if(!precise) {
+	if !precise {
 		// Tag does not point at HEAD; add hash and date to version.
-		run(&bmore, goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD", nil);
-		chomp(&bmore);
-		bwriteb(&b, &bmore);
+		tag += chomp(run(goroot, CheckExit, "git", "log", "-n", "1", "--format=format: +%h %cd", "HEAD"))
 	}
 
 	// Cache version.
-	writefile(&b, bstr(&path), 0);
-
-done:
-	p = btake(&b);
+	writefile(tag, path, 0)
 
-
-	bfree(&b);
-	bfree(&path);
-	bfree(&bmore);
-	bfree(&branch);
-
-	return p;
+	return tag
 }
 
 /*
@@ -360,7 +329,7 @@ done:
  */
 
 // The old tools that no longer live in $GOBIN or $GOROOT/bin.
-static char *oldtool[] = {
+var oldtool = []string{
 	"5a", "5c", "5g", "5l",
 	"6a", "6c", "6g", "6l",
 	"8a", "8c", "8g", "8l",
@@ -381,44 +350,40 @@ static char *oldtool[] = {
 	"govet",
 	"goyacc",
 	"quietgcc",
-};
+}
 
 // Unreleased directories (relative to $GOROOT) that should
 // not be in release branches.
-static char *unreleased[] = {
+var unreleased = []string{
 	"src/cmd/link",
 	"src/debug/goobj",
 	"src/old",
-};
+}
 
 // setup sets up the tree for the initial build.
-static void
-setup(void)
-{
-	int i;
-	Buf b;
-	char *p;
-
-	binit(&b);
-
+func setup() {
 	// Create bin directory.
-	p = bpathf(&b, "%s/bin", goroot);
-	if(!isdir(p))
-		xmkdir(p);
+	if p := pathf("%s/bin", goroot); !isdir(p) {
+		xmkdir(p)
+	}
 
 	// Create package directory.
-	p = bpathf(&b, "%s/pkg", goroot);
-	if(!isdir(p))
-		xmkdir(p);
-	p = bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch);
-	if(rebuildall)
-		xremoveall(p);
-	xmkdirall(p);
-	if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) {
-		p = bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch);
-		if(rebuildall)
-			xremoveall(p);
-		xmkdirall(p);
+	if p := pathf("%s/pkg", goroot); !isdir(p) {
+		xmkdir(p)
+	}
+
+	p := pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch)
+	if rebuildall {
+		xremoveall(p)
+	}
+	xmkdirall(p)
+
+	if goos != gohostos || goarch != gohostarch {
+		p := pathf("%s/pkg/%s_%s", goroot, goos, goarch)
+		if rebuildall {
+			xremoveall(p)
+		}
+		xmkdirall(p)
 	}
 
 	// Create object directory.
@@ -426,44 +391,48 @@ setup(void)
 	// are in one tree.  If pkg/obj/libgc.a exists, it is a dreg from
 	// before we used subdirectories of obj.  Delete all of obj
 	// to clean up.
-	bpathf(&b, "%s/pkg/obj/libgc.a", goroot);
-	if(isfile(bstr(&b)))
-		xremoveall(bpathf(&b, "%s/pkg/obj", goroot));
-	p = bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch);
-	if(rebuildall)
-		xremoveall(p);
-	xmkdirall(p);
+	if p := pathf("%s/pkg/obj/libgc.a", goroot); isfile(p) {
+		xremoveall(pathf("%s/pkg/obj", goroot))
+	}
+	p = pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch)
+	if rebuildall {
+		xremoveall(p)
+	}
+	xmkdirall(p)
 
 	// Create tool directory.
 	// We keep it in pkg/, just like the object directory above.
-	if(rebuildall)
-		xremoveall(tooldir);
-	xmkdirall(tooldir);
+	if rebuildall {
+		xremoveall(tooldir)
+	}
+	xmkdirall(tooldir)
 
 	// Remove tool binaries from before the tool/gohostos_gohostarch
-	xremoveall(bpathf(&b, "%s/bin/tool", goroot));
+	xremoveall(pathf("%s/bin/tool", goroot))
 
 	// Remove old pre-tool binaries.
-	for(i=0; i<nelem(oldtool); i++)
-		xremove(bpathf(&b, "%s/bin/%s", goroot, oldtool[i]));
+	for _, old := range oldtool {
+		xremove(pathf("%s/bin/%s", goroot, old))
+	}
 
 	// If $GOBIN is set and has a Go compiler, it must be cleaned.
-	for(i=0; gochars[i]; i++) {
-		if(isfile(bprintf(&b, "%s%s%c%s", gobin, slash, gochars[i], "g"))) {
-			for(i=0; i<nelem(oldtool); i++)
-				xremove(bprintf(&b, "%s%s%s", gobin, slash, oldtool[i]));
-			break;
+	for _, char := range gochars {
+		if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) {
+			for _, old := range oldtool {
+				xremove(pathf("%s/%s", gobin, old))
+			}
+			break
 		}
 	}
 
 	// For release, make sure excluded things are excluded.
-	if(hasprefix(goversion, "release.") || (hasprefix(goversion, "go") && !contains(goversion, "beta"))) {
-		for(i=0; i<nelem(unreleased); i++)
-			if(isdir(bpathf(&b, "%s/%s", goroot, unreleased[i])))
-				fatal("%s should not exist in release build", bstr(&b));
+	if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) {
+		for _, dir := range unreleased {
+			if p := pathf("%s/%s", goroot, dir); isdir(p) {
+				fatal("%s should not exist in release build", p)
+			}
+		}
 	}
-
-	bfree(&b);
 }
 
 /*
@@ -471,7 +440,7 @@ setup(void)
  */
 
 // gccargs is the gcc command line to use for compiling a single C file.
-static char *proto_gccargs[] = {
+var proto_gccargs = []string{
 	"-Wall",
 	// native Plan 9 compilers don't like non-standard prototypes
 	// so let gcc catch them.
@@ -489,34 +458,36 @@ static char *proto_gccargs[] = {
 	"-fno-common",
 	"-ggdb",
 	"-pipe",
-};
+}
 
 // gccargs2 is the second part of gccargs.
 // it is used if the environment isn't defining CFLAGS.
-static char *proto_gccargs2[] = {
+var proto_gccargs2 = []string{
 	// on older versions of GCC, -Wuninitialized is not supported
 	// without -O, so put it here together with -O settings in case
 	// the user's $CFLAGS doesn't include -O.
 	"-Wuninitialized",
-#if defined(__NetBSD__) && defined(__arm__)
-	// GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c
-	// Fix available at http://patchwork.ozlabs.org/patch/64562/.
-	"-O1",
-#else
 	"-O2",
-#endif
-};
+}
+
+func init() {
+	if runtime.GOOS == "netbsd" && runtime.GOARCH == "arm" {
+		// GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c
+		// Fix available at http://patchwork.ozlabs.org/patch/64562/.
+		proto_gccargs2[1] = "-O1"
+	}
+}
 
-static Vec gccargs, ldargs;
+var gccargs, ldargs []string
 
 // deptab lists changes to the default dependencies for a given prefix.
 // deps ending in /* read the whole directory; deps beginning with -
 // exclude files with that prefix.
-static struct {
-	char *prefix;  // prefix of target
-	char *dep[20];  // dependency tweaks for targets with that prefix
-} deptab[] = {
-	{"lib9", {
+var deptab = []struct {
+	prefix string   // prefix of target
+	dep    []string // dependency tweaks for targets with that prefix
+}{
+	{"lib9", []string{
 		"$GOROOT/include/u.h",
 		"$GOROOT/include/utf.h",
 		"$GOROOT/include/fmt.h",
@@ -524,14 +495,14 @@ static struct {
 		"fmt/*",
 		"utf/*",
 	}},
-	{"libbio", {
+	{"libbio", []string{
 		"$GOROOT/include/u.h",
 		"$GOROOT/include/utf.h",
 		"$GOROOT/include/fmt.h",
 		"$GOROOT/include/libc.h",
 		"$GOROOT/include/bio.h",
 	}},
-	{"liblink", {
+	{"liblink", []string{
 		"$GOROOT/include/u.h",
 		"$GOROOT/include/utf.h",
 		"$GOROOT/include/fmt.h",
@@ -544,85 +515,85 @@ static struct {
 		"anames8.c",
 		"anames9.c",
 	}},
-	{"cmd/gc", {
+	{"cmd/gc", []string{
 		"-cplx.c",
 		"-pgen.c",
 		"-plive.c",
 		"-popt.c",
-		"-y1.tab.c",  // makefile dreg
+		"-y1.tab.c", // makefile dreg
 		"opnames.h",
 	}},
-	{"cmd/5g", {
+	{"cmd/5g", []string{
 		"../gc/cplx.c",
 		"../gc/pgen.c",
 		"../gc/plive.c",
 		"../gc/popt.c",
 		"../gc/popt.h",
-		"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+		"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
 	}},
-	{"cmd/6g", {
+	{"cmd/6g", []string{
 		"../gc/cplx.c",
 		"../gc/pgen.c",
 		"../gc/plive.c",
 		"../gc/popt.c",
 		"../gc/popt.h",
-		"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+		"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
 	}},
-	{"cmd/8g", {
+	{"cmd/8g", []string{
 		"../gc/cplx.c",
 		"../gc/pgen.c",
 		"../gc/plive.c",
 		"../gc/popt.c",
 		"../gc/popt.h",
-		"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+		"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
 	}},
-	{"cmd/9g", {
+	{"cmd/9g", []string{
 		"../gc/cplx.c",
 		"../gc/pgen.c",
 		"../gc/plive.c",
 		"../gc/popt.c",
 		"../gc/popt.h",
-		"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libgc.a",
+		"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a",
 	}},
-	{"cmd/5l", {
+	{"cmd/5l", []string{
 		"../ld/*",
 	}},
-	{"cmd/6l", {
+	{"cmd/6l", []string{
 		"../ld/*",
 	}},
-	{"cmd/8l", {
+	{"cmd/8l", []string{
 		"../ld/*",
 	}},
-	{"cmd/9l", {
+	{"cmd/9l", []string{
 		"../ld/*",
 	}},
-	{"cmd/go", {
+	{"cmd/go", []string{
 		"zdefaultcc.go",
 	}},
-	{"cmd/", {
-		"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/liblink.a",
-		"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/libbio.a",
-		"$GOROOT/pkg/obj/$GOHOSTOS_$GOHOSTARCH/lib9.a",
+	{"cmd/", []string{
+		"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/liblink.a",
+		"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libbio.a",
+		"$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/lib9.a",
 	}},
-	{"runtime", {
+	{"runtime", []string{
 		"zaexperiment.h",
 		"zversion.go",
 	}},
-};
+}
 
 // depsuffix records the allowed suffixes for source files.
-char *depsuffix[] = {
+var depsuffix = []string{
 	".c",
 	".h",
 	".s",
 	".go",
-};
+}
 
 // gentab records how to generate some trivial files.
-static struct {
-	char *nameprefix;
-	void (*gen)(char*, char*);
-} gentab[] = {
+var gentab = []struct {
+	nameprefix string
+	gen        func(string, string)
+}{
 	{"opnames.h", gcopnames},
 	{"anames5.c", mkanames},
 	{"anames6.c", mkanames},
@@ -634,272 +605,238 @@ static struct {
 
 	// not generated anymore, but delete the file if we see it
 	{"enam.c", nil},
-};
+}
 
 // install installs the library, package, or binary associated with dir,
 // which is relative to $GOROOT/src.
-static void
-install(char *dir)
-{
-	char *name, *p, *elem, *prefix, *exe;
-	bool islib, ispkg, isgo, stale, ispackcmd;
-	Buf b, b1, path, final_path, final_name, archive;
-	Vec compile, files, link, go, missing, clean, lib, extra;
-	Time ttarg, t;
-	int i, j, k, n, doclean, targ;
-
-	if(vflag) {
-		if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
-			errprintf("%s (%s/%s)\n", dir, goos, goarch);
-		else
-			errprintf("%s\n", dir);
-	}
-
-	binit(&b);
-	binit(&b1);
-	binit(&path);
-	binit(&final_path);
-	binit(&final_name);
-	binit(&archive);
-	vinit(&compile);
-	vinit(&files);
-	vinit(&link);
-	vinit(&go);
-	vinit(&missing);
-	vinit(&clean);
-	vinit(&lib);
-	vinit(&extra);
+func install(dir string) {
+	if vflag > 0 {
+		if goos != gohostos || goarch != gohostarch {
+			errprintf("%s (%s/%s)\n", dir, goos, goarch)
+		} else {
+			errprintf("%s\n", dir)
+		}
+	}
 
+	var clean []string
+	defer func() {
+		for _, name := range clean {
+			xremove(name)
+		}
+	}()
 
 	// path = full path to dir.
-	bpathf(&path, "%s/src/%s", goroot, dir);
-	bpathf(&final_path, "%s/src/%s", goroot_final, dir);
-	name = lastelem(dir);
+	path := pathf("%s/src/%s", goroot, dir)
+	name := filepath.Base(dir)
 
 	// set up gcc command line on first run.
-	if(gccargs.len == 0) {
-		bprintf(&b, "%s %s", defaultcc, defaultcflags);
-		splitfields(&gccargs, bstr(&b));
-		for(i=0; i<nelem(proto_gccargs); i++)
-			vadd(&gccargs, proto_gccargs[i]);
-		if(defaultcflags[0] == '\0') {
-			for(i=0; i<nelem(proto_gccargs2); i++)
-				vadd(&gccargs, proto_gccargs2[i]);
+	if gccargs == nil {
+		gccargs = splitfields(defaultcc + " " + defaultcflags)
+		gccargs = append(gccargs, proto_gccargs...)
+		if defaultcflags == "" {
+			gccargs = append(gccargs, proto_gccargs2...)
 		}
-		if(contains(gccargs.p[0], "clang")) {
+		if strings.Contains(gccargs[0], "clang") {
 			// disable ASCII art in clang errors, if possible
-			vadd(&gccargs, "-fno-caret-diagnostics");
+			gccargs = append(gccargs, "-fno-caret-diagnostics")
 			// clang is too smart about unused command-line arguments
-			vadd(&gccargs, "-Qunused-arguments");
+			gccargs = append(gccargs, "-Qunused-arguments")
 		}
 		// disable word wrapping in error messages
-		vadd(&gccargs, "-fmessage-length=0");
-		if(streq(gohostos, "darwin")) {
+		gccargs = append(gccargs, "-fmessage-length=0")
+		if gohostos == "darwin" {
 			// golang.org/issue/5261
-			vadd(&gccargs, "-mmacosx-version-min=10.6");
+			gccargs = append(gccargs, "-mmacosx-version-min=10.6")
 		}
 	}
-	if(ldargs.len == 0 && defaultldflags[0] != '\0') {
-		bprintf(&b, "%s", defaultldflags);
-		splitfields(&ldargs, bstr(&b));
+	if ldargs == nil && defaultldflags != "" {
+		ldargs = splitfields(defaultldflags)
 	}
 
-	islib = hasprefix(dir, "lib") || streq(dir, "cmd/gc");
-	ispkg = !islib && !hasprefix(dir, "cmd/");
-	isgo = ispkg || streq(dir, "cmd/go") || streq(dir, "cmd/cgo");
+	islib := strings.HasPrefix(dir, "lib") || dir == "cmd/gc"
+	ispkg := !islib && !strings.HasPrefix(dir, "cmd/")
+	isgo := ispkg || dir == "cmd/go" || dir == "cmd/cgo"
 
-	exe = "";
-	if(streq(gohostos, "windows"))
-		exe = ".exe";
+	exe := ""
+	if gohostos == "windows" {
+		exe = ".exe"
+	}
 
 	// Start final link command line.
 	// Note: code below knows that link.p[targ] is the target.
-	ispackcmd = 0;
-	if(islib) {
+	var (
+		link      []string
+		targ      int
+		ispackcmd bool
+	)
+	switch {
+	case islib:
 		// C library.
-		vadd(&link, "ar");
-		if(streq(gohostos, "plan9"))
-			vadd(&link, "rc");
-		else
-			vadd(&link, "rsc");
-		prefix = "";
-		if(!hasprefix(name, "lib"))
-			prefix = "lib";
-		targ = link.len;
-		vadd(&link, bpathf(&b, "%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name));
-	} else if(ispkg) {
+		prefix := ""
+		if !strings.HasPrefix(name, "lib") {
+			prefix = "lib"
+		}
+		link = []string{"ar", "rsc", pathf("%s/pkg/obj/%s_%s/%s%s.a", goroot, gohostos, gohostarch, prefix, name)}
+		if gohostos == "plan9" {
+			link[1] = "rc"
+		}
+		targ = len(link) - 1
+
+	case ispkg:
 		// Go library (package).
-		ispackcmd = 1;
-		vadd(&link, "pack"); // program name - unused here, but all the other cases record one
-		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir);
-		*xstrrchr(p, '/') = '\0';
-		xmkdirall(p);
-		targ = link.len;
-		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir));
-	} else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) {
+		ispackcmd = true
+		link = []string{"pack", pathf("%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir)}
+		targ = len(link) - 1
+		xmkdirall(filepath.Dir(link[targ]))
+
+	case dir == "cmd/go" || dir == "cmd/cgo":
 		// Go command.
-		vadd(&link, bpathf(&b, "%s/%sl", tooldir, gochar));
-		vadd(&link, "-o");
-		elem = name;
-		if(streq(elem, "go"))
-			elem = "go_bootstrap";
-		targ = link.len;
-		vadd(&link, bpathf(&b, "%s/%s%s", tooldir, elem, exe));
-	} else {
+		elem := name
+		if elem == "go" {
+			elem = "go_bootstrap"
+		}
+		link = []string{fmt.Sprintf("%s/%sl", tooldir, gochar), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
+		targ = len(link) - 1
+
+	default:
 		// C command. Use gccargs and ldargs.
-		if(streq(gohostos, "plan9")) {
-			vadd(&link, bprintf(&b, "%sl", gohostchar));
-			vadd(&link, "-o");
-			targ = link.len;
-			vadd(&link, bpathf(&b, "%s/%s", tooldir, name));
+		if gohostos == "plan9" {
+			link = []string{fmt.Sprintf("%sl", gohostchar), "-o", pathf("%s/%s", tooldir, name)}
+			targ = len(link) - 1
 		} else {
-			vcopy(&link, gccargs.p, gccargs.len);
-			vcopy(&link, ldargs.p, ldargs.len);
-			if(sflag)
-				vadd(&link, "-static");
-			vadd(&link, "-o");
-			targ = link.len;
-			vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe));
-			if(streq(gohostarch, "amd64"))
-				vadd(&link, "-m64");
-			else if(streq(gohostarch, "386"))
-				vadd(&link, "-m32");
+			link = append(link, gccargs...)
+			link = append(link, ldargs...)
+			if sflag {
+				link = append(link, "-static")
+			}
+			link = append(link, "-o", pathf("%s/%s%s", tooldir, name, exe))
+			targ = len(link) - 1
+			switch gohostarch {
+			case "amd64":
+				link = append(link, "-m64")
+			case "386":
+				link = append(link, "-m32")
+			}
 		}
 	}
-	ttarg = mtime(link.p[targ]);
+	ttarg := mtime(link[targ])
 
 	// Gather files that are sources for this target.
 	// Everything in that directory, and any target-specific
 	// additions.
-	xreaddir(&files, bstr(&path));
+	files := xreaddir(path)
 
 	// Remove files beginning with . or _,
 	// which are likely to be editor temporary files.
 	// This is the same heuristic build.ScanDir uses.
 	// There do exist real C files beginning with _,
 	// so limit that check to just Go files.
-	n = 0;
-	for(i=0; i<files.len; i++) {
-		p = files.p[i];
-		if(hasprefix(p, ".") || (hasprefix(p, "_") && hassuffix(p, ".go")))
-			xfree(p);
-		else
-			files.p[n++] = p;
-	}
-	files.len = n;
-
-	for(i=0; i<nelem(deptab); i++) {
-		if(streq(dir, deptab[i].prefix) ||
-		   (hassuffix(deptab[i].prefix, "/") && hasprefix(dir, deptab[i].prefix))) {
-			for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
-				breset(&b1);
-				bwritestr(&b1, p);
-				bsubst(&b1, "$GOROOT", goroot);
-				bsubst(&b1, "$GOOS", goos);
-				bsubst(&b1, "$GOARCH", goarch);
-				bsubst(&b1, "$GOHOSTOS", gohostos);
-				bsubst(&b1, "$GOHOSTARCH", gohostarch);
-				p = bstr(&b1);
-				if(hassuffix(p, ".a")) {
-					vadd(&lib, bpathf(&b, "%s", p));
-					continue;
-				}
-				if(hassuffix(p, "/*")) {
-					bpathf(&b, "%s/%s", bstr(&path), p);
-					b.len -= 2;
-					xreaddir(&extra, bstr(&b));
-					bprintf(&b, "%s", p);
-					b.len -= 2;
-					for(k=0; k<extra.len; k++)
-						vadd(&files, bpathf(&b1, "%s/%s", bstr(&b), extra.p[k]));
-					continue;
-				}
-				if(hasprefix(p, "-")) {
-					p++;
-					n = 0;
-					for(k=0; k<files.len; k++) {
-						if(hasprefix(files.p[k], p))
-							xfree(files.p[k]);
-						else
-							files.p[n++] = files.p[k];
+	files = filter(files, func(p string) bool {
+		return !strings.HasPrefix(p, ".") && (!strings.HasPrefix(p, "_") || !strings.HasSuffix(p, ".go"))
+	})
+
+	var libs []string
+
+	for _, dt := range deptab {
+		if dir == dt.prefix || strings.HasSuffix(dt.prefix, "/") && strings.HasPrefix(dir, dt.prefix) {
+			for _, p := range dt.dep {
+				p = os.ExpandEnv(p)
+				switch {
+				case strings.HasSuffix(p, ".a"):
+					libs = append(libs, p)
+
+				case strings.HasSuffix(p, "/*"):
+					dir := strings.TrimSuffix(p, "/*")
+					for _, name := range xreaddir(pathf("%s/%s", path, dir)) {
+						files = append(files, pathf("%s/%s", dir, name))
 					}
-					files.len = n;
-					continue;
+
+				case strings.HasPrefix(p, "-"):
+					files = filter(files, func(s string) bool {
+						return !strings.HasPrefix(s, p[1:])
+					})
+
+				default:
+					files = append(files, p)
 				}
-				vadd(&files, p);
 			}
 		}
 	}
-	vuniq(&files);
+	files = uniq(files)
 
 	// Convert to absolute paths.
-	for(i=0; i<files.len; i++) {
-		if(!isabs(files.p[i])) {
-			bpathf(&b, "%s/%s", bstr(&path), files.p[i]);
-			xfree(files.p[i]);
-			files.p[i] = btake(&b);
+	for i, p := range files {
+		if !isabs(p) {
+			files[i] = pathf("%s/%s", path, p)
 		}
 	}
 
 	// Is the target up-to-date?
-	stale = rebuildall;
-	n = 0;
-	for(i=0; i<files.len; i++) {
-		p = files.p[i];
-		for(j=0; j<nelem(depsuffix); j++)
-			if(hassuffix(p, depsuffix[j]))
-				goto ok;
-		xfree(files.p[i]);
-		continue;
+	var gofiles, missing []string
+	stale := rebuildall
+	files = filter(files, func(p string) bool {
+		for _, suf := range depsuffix {
+			if strings.HasSuffix(p, suf) {
+				goto ok
+			}
+		}
+		return false
 	ok:
-		t = mtime(p);
-		if(t != 0 && !hassuffix(p, ".a") && !shouldbuild(p, dir)) {
-			xfree(files.p[i]);
-			continue;
+		t := mtime(p)
+		if !t.IsZero() && !strings.HasSuffix(p, ".a") && !shouldbuild(p, dir) {
+			return false
 		}
-		if(hassuffix(p, ".go"))
-			vadd(&go, p);
-		if(t > ttarg)
-			stale = 1;
-		if(t == 0) {
-			vadd(&missing, p);
-			files.p[n++] = files.p[i];
-			continue;
+		if strings.HasSuffix(p, ".go") {
+			gofiles = append(gofiles, p)
 		}
-		files.p[n++] = files.p[i];
-	}
-	files.len = n;
+		if t.After(ttarg) {
+			stale = true
+		}
+		if t.IsZero() {
+			missing = append(missing, p)
+		}
+		return true
+	})
 
 	// If there are no files to compile, we're done.
-	if(files.len == 0)
-		goto out;
-	
-	for(i=0; i<lib.len && !stale; i++)
-		if(mtime(lib.p[i]) > ttarg)
-			stale = 1;
+	if len(files) == 0 {
+		return
+	}
 
-	if(!stale)
-		goto out;
+	if !stale {
+		for _, p := range libs {
+			if mtime(p).After(ttarg) {
+				stale = true
+				break
+			}
+		}
+	}
+
+	if !stale {
+		return
+	}
 
 	// For package runtime, copy some files into the work space.
-	if(streq(dir, "runtime")) {
-		copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
-			bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0);
-		copyfile(bpathf(&b, "%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch),
-			bpathf(&b1, "%s/src/runtime/funcdata.h", goroot), 0);
+	if dir == "runtime" {
+		// For use by assembly and C files.
+		copyfile(pathf("%s/pkg/%s_%s/textflag.h", goroot, goos, goarch),
+			pathf("%s/src/cmd/ld/textflag.h", goroot), 0)
+		copyfile(pathf("%s/pkg/%s_%s/funcdata.h", goroot, goos, goarch),
+			pathf("%s/src/runtime/funcdata.h", goroot), 0)
 	}
 
 	// 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(gentab[j].gen == nil)
-				continue;
-			if(hasprefix(elem, gentab[j].nameprefix)) {
-				if(vflag > 1)
-					errprintf("generate %s\n", p);
-				gentab[j].gen(bstr(&path), p);
+	for _, p := range files {
+		elem := filepath.Base(p)
+		for _, gt := range gentab {
+			if gt.gen == nil {
+				continue
+			}
+			if strings.HasPrefix(elem, gt.nameprefix) {
+				if vflag > 1 {
+					errprintf("generate %s\n", p)
+				}
+				gt.gen(path, p)
 				// Do not add generated file to clean list.
 				// In runtime, we want to be able to
 				// build the package with the go tool,
@@ -907,222 +844,173 @@ install(char *dir)
 				// exist (it does not know how to build them).
 				// The 'clean' command can remove
 				// the generated files.
-				goto built;
+				goto built
 			}
 		}
 		// Did not rebuild p.
-		if(find(p, missing.p, missing.len) >= 0)
-			fatal("missing file %s", p);
-	built:;
+		if find(p, missing) >= 0 {
+			fatal("missing file %s", p)
+		}
+	built:
 	}
 
-	if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) {
+	if (goos != gohostos || goarch != gohostarch) && isgo {
 		// We've generated the right files; the go command can do the build.
-		if(vflag > 1)
-			errprintf("skip build for cross-compile %s\n", dir);
-		goto nobuild;
+		if vflag > 1 {
+			errprintf("skip build for cross-compile %s\n", dir)
+		}
+		return
 	}
 
-	if(isgo) {
+	var archive string
+	if isgo {
 		// The next loop will compile individual non-Go files.
 		// Hand the Go files to the compiler en masse.
 		// For package runtime, this writes go_asm.h, which
 		// the assembly files will need.
-		vreset(&compile);
-		vadd(&compile, bpathf(&b, "%s/%sg", tooldir, gochar));
-
-		bpathf(&b, "%s/_go_.a", workdir);
-		vadd(&compile, "-pack");
-		vadd(&compile, "-o");
-		vadd(&compile, bstr(&b));
-		vadd(&clean, bstr(&b));
-		if(!ispackcmd)
-			vadd(&link, bstr(&b));
-		else
-			bwriteb(&archive, &b);
-
-		vadd(&compile, "-p");
-		if(hasprefix(dir, "cmd/"))
-			vadd(&compile, "main");
-		else
-			vadd(&compile, dir);
-
-		if(streq(dir, "runtime")) {
-			vadd(&compile, "-+");
-			vadd(&compile, "-asmhdr");
-			bpathf(&b1, "%s/go_asm.h", workdir);
-			vadd(&compile, bstr(&b1));
+		pkg := dir
+		if strings.HasPrefix(dir, "cmd/") {
+			pkg = "main"
 		}
-
-		vcopy(&compile, go.p, go.len);
-
-		runv(nil, bstr(&path), CheckExit, &compile);
+		b := pathf("%s/_go_.a", workdir)
+		clean = append(clean, b)
+		if !ispackcmd {
+			link = append(link, b)
+		} else {
+			archive = b
+		}
+		compile := []string{pathf("%s/%sg", tooldir, gochar), "-pack", "-o", b, "-p", pkg}
+		if dir == "runtime" {
+			compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir))
+		}
+		compile = append(compile, gofiles...)
+		run(path, CheckExit|ShowOutput, compile...)
 	}
 
 	// Compile the files.
-	for(i=0; i<files.len; i++) {
-		if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s"))
-			continue;
-		name = lastelem(files.p[i]);
+	for _, p := range files {
+		if !strings.HasSuffix(p, ".c") && !strings.HasSuffix(p, ".s") {
+			continue
+		}
+		name := filepath.Base(p)
 
-		vreset(&compile);
-		if(!isgo) {
+		var compile []string
+		if !isgo {
 			// C library or tool.
-			if(streq(gohostos, "plan9")) {
-				vadd(&compile, bprintf(&b, "%sc", gohostchar));
-				vadd(&compile, "-FTVwp");
-				vadd(&compile, "-DPLAN9");
-				vadd(&compile, "-D__STDC__=1");
-				vadd(&compile, "-D__SIZE_TYPE__=ulong"); // for GNU Bison
-				vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot));
-				vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch));
+			if gohostos == "plan9" {
+				compile = []string{
+					gohostchar + "c", "-FTVwp",
+					"-DPLAN9",
+					"-D__STDC__=1",
+					"-D__SIZE_TYPE__=ulong", // for GNU bison
+					pathf("-I%s/include/plan9", goroot),
+					pathf("-I%s/include/plan9/%s", goroot, gohostarch),
+				}
 			} else {
-				vcopy(&compile, gccargs.p, gccargs.len);
-				vadd(&compile, "-c");
-				if(streq(gohostarch, "amd64"))
-					vadd(&compile, "-m64");
-				else if(streq(gohostarch, "386"))
-					vadd(&compile, "-m32");
-	
-				vadd(&compile, "-I");
-				vadd(&compile, bpathf(&b, "%s/include", goroot));
+				compile = gccargs[0:len(gccargs):len(gccargs)]
+				compile = append(compile, "-c")
+				switch gohostarch {
+				case "amd64":
+					compile = append(compile, "-m64")
+				case "386":
+					compile = append(compile, "-m32")
+				}
+				compile = append(compile, "-I", pathf("%s/include", goroot))
 			}
 
-			if(streq(dir, "lib9"))
-				vadd(&compile, "-DPLAN9PORT");
-
+			if dir == "lib9" {
+				compile = append(compile, "-DPLAN9PORT")
+			}
 
-			vadd(&compile, "-I");
-			vadd(&compile, bstr(&path));
+			compile = append(compile, "-I", path)
 
 			// lib9/goos.c gets the default constants hard-coded.
-			if(streq(name, "goos.c")) {
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b, "GOOS=\"%s\"", goos));
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b, "GOARCH=\"%s\"", goarch));
-				bprintf(&b1, "%s", goroot_final);
-				bsubst(&b1, "\\", "\\\\");  // turn into C string
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b, "GOROOT=\"%s\"", bstr(&b1)));
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b, "GOVERSION=\"%s\"", goversion));
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b, "GOARM=\"%s\"", goarm));
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b, "GO386=\"%s\"", go386));
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b, "GO_EXTLINK_ENABLED=\"%s\"", goextlinkenabled));
+			if name == "goos.c" {
+				compile = append(compile,
+					"-D", fmt.Sprintf("GOOS=%q", goos),
+					"-D", fmt.Sprintf("GOARCH=%q", goarch),
+					"-D", fmt.Sprintf("GOROOT=%q", goroot_final),
+					"-D", fmt.Sprintf("GOVERSION=%q", goversion),
+					"-D", fmt.Sprintf("GOARM=%q", goarm),
+					"-D", fmt.Sprintf("GO386=%q", go386),
+					"-D", fmt.Sprintf("GO_EXTLINK_ENABLED=%q", goextlinkenabled),
+				)
 			}
 
 			// gc/lex.c records the GOEXPERIMENT setting used during the build.
-			if(streq(name, "lex.c")) {
-				xgetenv(&b, "GOEXPERIMENT");
-				vadd(&compile, "-D");
-				vadd(&compile, bprintf(&b1, "GOEXPERIMENT=\"%s\"", bstr(&b)));
+			if name == "lex.c" {
+				compile = append(compile,
+					"-D", fmt.Sprintf("GOEXPERIMENT=%q", os.Getenv("GOEXPERIMENT")))
 			}
 		} else {
-			// Supporting files for a Go package.
-			if(hassuffix(files.p[i], ".s"))
-				vadd(&compile, bpathf(&b, "%s/%sa", tooldir, gochar));
-			else {
-				vadd(&compile, bpathf(&b, "%s/%sc", tooldir, gochar));
-				vadd(&compile, "-F");
-				vadd(&compile, "-V");
-				vadd(&compile, "-w");
+			// Assembly file for a Go package.
+			compile = []string{
+				pathf("%s/%sa", tooldir, gochar),
+				"-I", workdir,
+				"-I", pathf("%s/pkg/%s_%s", goroot, goos, goarch),
+				"-D", "GOOS_" + goos,
+				"-D", "GOARCH_" + goarch,
+				"-D", "GOOS_GOARCH_" + goos + "_" + goarch,
 			}
-			vadd(&compile, "-I");
-			vadd(&compile, workdir);
-			vadd(&compile, "-I");
-			vadd(&compile, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
-			vadd(&compile, "-D");
-			vadd(&compile, bprintf(&b, "GOOS_%s", goos));
-			vadd(&compile, "-D");
-			vadd(&compile, bprintf(&b, "GOARCH_%s", goarch));
-			vadd(&compile, "-D");
-			vadd(&compile, bprintf(&b, "GOOS_GOARCH_%s_%s", goos, goarch));
 		}
 
-		bpathf(&b, "%s/%s", workdir, lastelem(files.p[i]));
-		doclean = 1;
-		if(!isgo && streq(gohostos, "darwin")) {
+		doclean := true
+		b := pathf("%s/%s", workdir, filepath.Base(p))
+		if !isgo && 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]));
-			doclean = 0;
+			obj := pathf("%s/pkg/obj/%s", goroot, dir)
+			xmkdirall(obj)
+			b = pathf("%s/%s", obj, filepath.Base(p))
+			doclean = false
 		}
 
 		// Change the last character of the output file (which was c or s).
-		if(streq(gohostos, "plan9"))
-			b.p[b.len-1] = gohostchar[0];
-		else
-			b.p[b.len-1] = 'o';
-		vadd(&compile, "-o");
-		vadd(&compile, bstr(&b));
-		vadd(&compile, files.p[i]);
-		bgrunv(bstr(&path), CheckExit, &compile);
+		if gohostos == "plan9" {
+			b = b[:len(b)-1] + gohostchar
+		} else {
+			b = b[:len(b)-1] + "o"
+		}
+		compile = append(compile, "-o", b, p)
+		bgrun(path, compile...)
 
-		vadd(&link, bstr(&b));
-		if(doclean)
-			vadd(&clean, bstr(&b));
+		link = append(link, b)
+		if doclean {
+			clean = append(clean, b)
+		}
 	}
-	bgwait();
+	bgwait()
 
-	if(isgo && ispackcmd) {
-		xremove(link.p[targ]);
-		dopack(link.p[targ], bstr(&archive), &link.p[targ+1], link.len - (targ+1));
-		goto nobuild;
+	if isgo && ispackcmd {
+		xremove(link[targ])
+		dopack(link[targ], archive, link[targ+1:])
+		return
 	}
 
-	if(!islib && !isgo) {
+	if !islib && !isgo {
 		// C binaries need the libraries explicitly, and -lm.
-		vcopy(&link, lib.p, lib.len);
-		if(!streq(gohostos, "plan9"))
-			vadd(&link, "-lm");
+		link = append(link, libs...)
+		if gohostos != "plan9" {
+			link = append(link, "-lm")
+		}
 	}
 
 	// Remove target before writing it.
-	xremove(link.p[targ]);
-
-	runv(nil, nil, CheckExit, &link);
-nobuild:
-
-out:
-	for(i=0; i<clean.len; i++)
-		xremove(clean.p[i]);
-
-	bfree(&b);
-	bfree(&b1);
-	bfree(&path);
-	bfree(&archive);
-	vfree(&compile);
-	vfree(&files);
-	vfree(&link);
-	vfree(&go);
-	vfree(&missing);
-	vfree(&clean);
-	vfree(&lib);
-	vfree(&extra);
+	xremove(link[targ])
+	run("", CheckExit|ShowOutput, link...)
 }
 
 // matchfield reports whether the field matches this build.
-static bool
-matchfield(char *f)
-{
-	char *p;
-	bool res;
-
-	p = xstrrchr(f, ',');
-	if(p == nil)
-		return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1") || (streq(goos, "android") && streq(f, "linux"));
-	*p = 0;
-	res = matchfield(f) && matchfield(p+1);
-	*p = ',';
-	return res;
+func matchfield(f string) bool {
+	for _, tag := range strings.Split(f, ",") {
+		if tag == goos || tag == goarch || tag == "cmd_go_bootstrap" || tag == "go1.1" || (goos == "android" && tag == "linux") {
+			continue
+		}
+		return false
+	}
+	return true
 }
 
 // shouldbuild reports whether we should build this file.
@@ -1132,162 +1020,117 @@ matchfield(char *f)
 // In particular, they can be the entire file name (like windows.c).
 // We also allow the special tag cmd_go_bootstrap.
 // See ../go/bootstrap.go and package go/build.
-static bool
-shouldbuild(char *file, char *dir)
-{
-	char *name, *p;
-	int i, j, ret;
-	Buf b;
-	Vec lines, fields;
-	
+func shouldbuild(file, dir string) bool {
 	// Check file name for GOOS or GOARCH.
-	name = lastelem(file);
-	for(i=0; i<nelem(okgoos); i++) {
-		if(streq(okgoos[i], goos))
-			continue;
-		p = xstrstr(name, okgoos[i]);
-		if(p == nil)
-			continue;
-		p += xstrlen(okgoos[i]);
-		if(*p == '.' || *p == '_' || *p == '\0')
-			return 0;
-	}
-	for(i=0; i<nelem(okgoarch); i++) {
-		if(streq(okgoarch[i], goarch))
-			continue;
-		p = xstrstr(name, okgoarch[i]);
-		if(p == nil)
-			continue;
-		p += xstrlen(okgoarch[i]);
-		if(*p == '.' || *p == '_' || *p == '\0')
-			return 0;
+	name := filepath.Base(file)
+	excluded := func(list []string, ok string) bool {
+		for _, x := range list {
+			if x == ok {
+				continue
+			}
+			i := strings.Index(name, x)
+			if i < 0 {
+				continue
+			}
+			i += len(x)
+			if i == len(name) || name[i] == '.' || name[i] == '_' {
+				return true
+			}
+		}
+		return false
+	}
+	if excluded(okgoos, goos) || excluded(okgoarch, goarch) {
+		return false
 	}
 
 	// Omit test files.
-	if(contains(name, "_test"))
-		return 0;
+	if strings.Contains(name, "_test") {
+		return false
+	}
 
 	// 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;
-	if(hassuffix(file, "cmd/cgo/doc.go") || hassuffix(file, "cmd\\cgo\\doc.go"))
-		return 0;
+	if strings.HasSuffix(file, "cmd/go/doc.go") || strings.HasSuffix(file, "cmd\\go\\doc.go") {
+		return false
+	}
+	if strings.HasSuffix(file, "cmd/cgo/doc.go") || strings.HasSuffix(file, "cmd\\cgo\\doc.go") {
+		return false
+	}
 
 	// Check file contents for // +build lines.
-	binit(&b);
-	vinit(&lines);
-	vinit(&fields);
-
-	ret = 1;
-	readfile(&b, file);
-	splitlines(&lines, bstr(&b));
-	for(i=0; i<lines.len; i++) {
-		p = lines.p[i];
-		while(*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
-			p++;
-		if(*p == '\0')
-			continue;
-		if(contains(p, "package documentation")) {
-			ret = 0;
-			goto out;
+	for _, p := range splitlines(readfile(file)) {
+		p = strings.TrimSpace(p)
+		if p == "" {
+			continue
+		}
+		if strings.Contains(p, "package documentation") {
+			return false
 		}
-		if(contains(p, "package main") && !streq(dir, "cmd/go") && !streq(dir, "cmd/cgo")) {
-			ret = 0;
-			goto out;
+		if strings.Contains(p, "package main") && dir != "cmd/go" && dir != "cmd/cgo" {
+			return false
 		}
-		if(!hasprefix(p, "//"))
-			break;
-		if(!contains(p, "+build"))
-			continue;
-		splitfields(&fields, lines.p[i]);
-		if(fields.len < 2 || !streq(fields.p[1], "+build"))
-			continue;
-		for(j=2; j<fields.len; j++) {
-			p = fields.p[j];
-			if((*p == '!' && !matchfield(p+1)) || matchfield(p))
-				goto fieldmatch;
+		if !strings.HasPrefix(p, "//") {
+			break
 		}
-		ret = 0;
-		goto out;
-	fieldmatch:;
+		if !strings.Contains(p, "+build") {
+			continue
+		}
+		fields := splitfields(p)
+		if len(fields) < 2 || fields[1] != "+build" {
+			continue
+		}
+		for _, p := range fields[2:] {
+			if (p[0] == '!' && !matchfield(p[1:])) || matchfield(p) {
+				goto fieldmatch
+			}
+		}
+		return false
+	fieldmatch:
 	}
 
-out:
-	bfree(&b);
-	vfree(&lines);
-	vfree(&fields);
-
-	return ret;
+	return true
 }
 
 // copy copies the file src to dst, via memory (so only good for small files).
-void
-copyfile(char *dst, char *src, int exec)
-{
-	Buf b;
-
-	if(vflag > 1)
-		errprintf("cp %s %s\n", src, dst);
-
-	binit(&b);
-	readfile(&b, src);
-	writefile(&b, dst, exec);
-	bfree(&b);
+func copyfile(dst, src string, exec int) {
+	if vflag > 1 {
+		errprintf("cp %s %s\n", src, dst)
+	}
+	writefile(readfile(src), dst, exec)
 }
 
 // dopack copies the package src to dst,
 // appending the files listed in extra.
 // The archive format is the traditional Unix ar format.
-static void
-dopack(char *dst, char *src, char **extra, int nextra)
-{
-	int i;
-	char c, *p, *q;
-	Buf b, bdst;
-	
-	binit(&b);
-	binit(&bdst);
-
-	readfile(&bdst, src);
-	for(i=0; i<nextra; i++) {
-		readfile(&b, extra[i]);
+func dopack(dst, src string, extra []string) {
+	bdst := bytes.NewBufferString(readfile(src))
+	for _, file := range extra {
+		b := readfile(file)
 		// find last path element for archive member name
-		p = xstrrchr(extra[i], '/');
-		if(p)
-			p++;
-		q = xstrrchr(extra[i], '\\');
-		if(q) {
-			q++;
-			if(p == nil || q > p)
-				p = q;
+		i := strings.LastIndex(file, "/") + 1
+		j := strings.LastIndex(file, `\`) + 1
+		if i < j {
+			i = j
 		}
-		if(p == nil)
-			p = extra[i];
-		bwritef(&bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", p, 0, 0, 0, 0644, b.len);
-		bwriteb(&bdst, &b);
-		if(b.len&1) {
-			c = 0;
-			bwrite(&bdst, &c, 1);
+		fmt.Fprintf(bdst, "%-16.16s%-12d%-6d%-6d%-8o%-10d`\n", file[i:], 0, 0, 0, 0644, len(b))
+		bdst.WriteString(b)
+		if len(b)&1 != 0 {
+			bdst.WriteByte(0)
 		}
 	}
-
-	writefile(&bdst, dst, 0);
-
-	bfree(&b);
-	bfree(&bdst);
+	writefile(bdst.String(), dst, 0)
 }
 
 // buildorder records the order of builds for the 'go bootstrap' command.
-static char *buildorder[] = {
+var buildorder = []string{
 	"lib9",
 	"libbio",
 	"liblink",
 
 	"cmd/gc",  // must be before g
-	"cmd/%sl",  // must be before a, g
+	"cmd/%sl", // must be before a, g
 	"cmd/%sa",
 	"cmd/%sg",
 
@@ -1337,12 +1180,12 @@ static char *buildorder[] = {
 	"go/doc",
 	"go/build",
 	"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[] = {
+var cleantab = []string{
 	// Commands and C libraries.
 	"cmd/5a",
 	"cmd/5g",
@@ -1357,7 +1200,7 @@ static char *cleantab[] = {
 	"cmd/9g",
 	"cmd/9l",
 	"cmd/gc",
-	"cmd/go",	
+	"cmd/go",
 	"lib9",
 	"libbio",
 	"liblink",
@@ -1403,383 +1246,246 @@ static char *cleantab[] = {
 	"unicode",
 	"unicode/utf16",
 	"unicode/utf8",
-};
+}
 
-static char *runtimegen[] = {
+var runtimegen = []string{
 	"zaexperiment.h",
 	"zversion.go",
-};
-
-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));
+}
+
+func clean() {
+	for _, name := range cleantab {
+		path := pathf("%s/src/%s", goroot, name)
 		// 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]));
+		for _, elem := range xreaddir(path) {
+			for _, gt := range gentab {
+				if strings.HasPrefix(elem, gt.nameprefix) {
+					xremove(pathf("%s/%s", path, elem))
+				}
 			}
 		}
 		// Remove generated binary named for directory.
-		if(hasprefix(cleantab[i], "cmd/"))
-			xremove(bpathf(&b, "%s/%s", bstr(&path), cleantab[i]+4));
+		if strings.HasPrefix(name, "cmd/") {
+			xremove(pathf("%s/%s", path, name[4:]))
+		}
 	}
 
-	// remove src/runtime/zaexperiment.h and 
-	// except leave zgoos and zgoarch, now maintained with go generate.
-	bpathf(&path, "%s/src/runtime", goroot);
-	for(j=0; j<nelem(runtimegen); j++)
-		xremove(bpathf(&b, "%s/%s", bstr(&path), runtimegen[j]));
+	// remove runtimegen files.
+	path := pathf("%s/src/runtime", goroot)
+	for _, elem := range runtimegen {
+		xremove(pathf("%s/%s", path, elem))
+	}
 
-	if(rebuildall) {
+	if rebuildall {
 		// Remove object tree.
-		xremoveall(bpathf(&b, "%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch));
+		xremoveall(pathf("%s/pkg/obj/%s_%s", goroot, gohostos, gohostarch))
 
 		// Remove installed packages and tools.
-		xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, gohostos, gohostarch));
-		xremoveall(bpathf(&b, "%s/pkg/%s_%s", goroot, goos, goarch));
-		xremoveall(tooldir);
+		xremoveall(pathf("%s/pkg/%s_%s", goroot, gohostos, gohostarch))
+		xremoveall(pathf("%s/pkg/%s_%s", goroot, goos, goarch))
+		xremoveall(tooldir)
 
 		// Remove cached version info.
-		xremove(bpathf(&b, "%s/VERSION.cache", goroot));
+		xremove(pathf("%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);
+func usage() {
+	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\"\n";
-	pflag = 0;
-	ARGBEGIN{
-	case '9':
-		format = "%s='%s'\n";
-		break;
-	case 'p':
-		pflag = 1;
-		break;
-	case 'v':
-		vflag++;
-		break;
-	case 'w':
-		format = "set %s=%s\r\n";
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if(argc > 0)
-		usage();
-
-	xprintf(format, "CC", defaultcc);
-	xprintf(format, "CC_FOR_TARGET", defaultcctarget);
-	xprintf(format, "GOROOT", goroot);
-	xprintf(format, "GOBIN", gobin);
-	xprintf(format, "GOARCH", goarch);
-	xprintf(format, "GOOS", goos);
-	xprintf(format, "GOHOSTARCH", gohostarch);
-	xprintf(format, "GOHOSTOS", gohostos);
-	xprintf(format, "GOTOOLDIR", tooldir);
-	xprintf(format, "GOCHAR", gochar);
-	if(streq(goarch, "arm"))
-		xprintf(format, "GOARM", goarm);
-	if(streq(goarch, "386"))
-		xprintf(format, "GO386", go386);
-
-	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);
+func cmdenv() {
+	path := flag.Bool("p", false, "emit updated PATH")
+	plan9 := flag.Bool("9", false, "emit plan 9 syntax")
+	windows := flag.Bool("w", false, "emit windows syntax")
+	xflagparse(0)
+
+	format := "%s=\"%s\"\n"
+	switch {
+	case *plan9:
+		format = "%s='%s'\n"
+	case *windows:
+		format = "set %s=%s\r\n"
+	}
+
+	xprintf(format, "CC", defaultcc)
+	xprintf(format, "CC_FOR_TARGET", defaultcctarget)
+	xprintf(format, "GOROOT", goroot)
+	xprintf(format, "GOBIN", gobin)
+	xprintf(format, "GOARCH", goarch)
+	xprintf(format, "GOOS", goos)
+	xprintf(format, "GOHOSTARCH", gohostarch)
+	xprintf(format, "GOHOSTOS", gohostos)
+	xprintf(format, "GOTOOLDIR", tooldir)
+	xprintf(format, "GOCHAR", gochar)
+	if goarch == "arm" {
+		xprintf(format, "GOARM", goarm)
+	}
+	if goarch == "386" {
+		xprintf(format, "GO386", go386)
+	}
+
+	if *path {
+		sep := ":"
+		if gohostos == "windows" {
+			sep = ";"
+		}
+		xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH")))
+	}
 }
 
 // The bootstrap command runs a build from scratch,
 // stopping at having installed the go_bootstrap command.
-void
-cmdbootstrap(int argc, char **argv)
-{
-	int i;
-	Buf b;
-	char *oldgoos, *oldgoarch, *oldgochar;
-
-	binit(&b);
-
-	ARGBEGIN{
-	case 'a':
-		rebuildall = 1;
-		break;
-	case 's':
-		sflag++;
-		break;
-	case 'v':
-		vflag++;
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if(argc > 0)
-		usage();
+func cmdbootstrap() {
+	flag.BoolVar(&rebuildall, "a", rebuildall, "rebuild all")
+	flag.BoolVar(&sflag, "s", sflag, "build static binaries")
+	xflagparse(0)
+
+	if isdir(pathf("%s/src/pkg", goroot)) {
+		fatal("\n\n"+
+			"The Go package sources have moved to $GOROOT/src.\n"+
+			"*** %s still exists. ***\n"+
+			"It probably contains stale files that may confuse the build.\n"+
+			"Please (check what's there and) remove it and try again.\n"+
+			"See http://golang.org/s/go14nopkg\n",
+			pathf("%s/src/pkg", goroot))
+	}
 
-	if(isdir(bpathf(&b, "%s/src/pkg", goroot))) {
-		fatal("\n\n"
-			"The Go package sources have moved to $GOROOT/src.\n"
-			"*** %s still exists. ***\n"
-			"It probably contains stale files that may confuse the build.\n"
-			"Please (check what's there and) remove it and try again.\n"
-			"See http://golang.org/s/go14nopkg\n", bpathf(&b, "%s/src/pkg", goroot));
+	if rebuildall {
+		clean()
 	}
-	
-	if(rebuildall)
-		clean();
-	goversion = findgoversion();
-	setup();
 
-	xsetenv("GOROOT", goroot);
-	xsetenv("GOROOT_FINAL", goroot_final);
+	setup()
 
 	// For the main bootstrap, building for host os/arch.
-	oldgoos = goos;
-	oldgoarch = goarch;
-	oldgochar = gochar;
-	goos = gohostos;
-	goarch = gohostarch;
-	gochar = gohostchar;
-	xsetenv("GOARCH", goarch);
-	xsetenv("GOOS", goos);
-
-	for(i=0; i<nelem(buildorder); i++) {
-		install(bprintf(&b, buildorder[i], gohostchar));
-		if(!streq(oldgochar, gohostchar) && xstrstr(buildorder[i], "%s"))
-			install(bprintf(&b, buildorder[i], oldgochar));
-	}
-
-	goos = oldgoos;
-	goarch = oldgoarch;
-	gochar = oldgochar;
-	xsetenv("GOARCH", goarch);
-	xsetenv("GOOS", goos);
+	oldgoos = goos
+	oldgoarch = goarch
+	oldgochar = gochar
+	goos = gohostos
+	goarch = gohostarch
+	gochar = gohostchar
+	os.Setenv("GOHOSTARCH", gohostarch)
+	os.Setenv("GOHOSTOS", gohostos)
+	os.Setenv("GOARCH", goarch)
+	os.Setenv("GOOS", goos)
+
+	for _, pattern := range buildorder {
+		dir := pattern
+		if strings.Contains(pattern, "%s") {
+			dir = fmt.Sprintf(pattern, gohostchar)
+		}
+		install(dir)
+		if oldgochar != gohostchar && strings.Contains(pattern, "%s") {
+			install(fmt.Sprintf(pattern, oldgochar))
+		}
+	}
 
-	// Build runtime for actual goos/goarch too.
-	if(!streq(goos, gohostos) || !streq(goarch, gohostarch))
-		install("runtime");
+	goos = oldgoos
+	goarch = oldgoarch
+	gochar = oldgochar
+	os.Setenv("GOARCH", goarch)
+	os.Setenv("GOOS", goos)
 
-	bfree(&b);
+	// Build runtime for actual goos/goarch too.
+	if goos != gohostos || goarch != gohostarch {
+		install("runtime")
+	}
 }
 
-static char*
-defaulttarg(void)
-{
-	char *p;
-	Buf pwd, src, real_src;
-
-	binit(&pwd);
-	binit(&src);
-	binit(&real_src);
-
+func defaulttarg() string {
 	// xgetwd might return a path with symlinks fully resolved, and if
 	// there happens to be symlinks in goroot, then the hasprefix test
 	// will never succeed. Instead, we use xrealwd to get a canonical
 	// goroot/src before the comparison to avoid this problem.
-	xgetwd(&pwd);
-	p = btake(&pwd);
-	bpathf(&src, "%s/src/", goroot);
-	xrealwd(&real_src, bstr(&src));
-	if(!hasprefix(p, bstr(&real_src)))
-		fatal("current directory %s is not under %s", p, bstr(&real_src));
-	p += real_src.len;
+	pwd := xgetwd()
+	src := pathf("%s/src/", goroot)
+	real_src := xrealwd(src)
+	if !strings.HasPrefix(pwd, real_src) {
+		fatal("current directory %s is not under %s", pwd, real_src)
+	}
+	pwd = pwd[len(real_src):]
 	// guard againt xrealwd return the directory without the trailing /
-	if(*p == slash[0])
-		p++;
-
-	bfree(&pwd);
-	bfree(&src);
-	bfree(&real_src);
+	pwd = strings.TrimPrefix(pwd, "/")
 
-	return p;
+	return pwd
 }
 
 // Install installs the list of packages named on the command line.
-void
-cmdinstall(int argc, char **argv)
-{
-	int i;
-
-	ARGBEGIN{
-	case 's':
-		sflag++;
-		break;
-	case 'v':
-		vflag++;
-		break;
-	default:
-		usage();
-	}ARGEND
+func cmdinstall() {
+	flag.BoolVar(&sflag, "s", sflag, "build static binaries")
+	xflagparse(-1)
 
-	if(argc == 0)
-		install(defaulttarg());
+	if flag.NArg() == 0 {
+		install(defaulttarg())
+	}
 
-	for(i=0; i<argc; i++)
-		install(argv[i]);
+	for _, arg := range flag.Args() {
+		install(arg)
+	}
 }
 
 // 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();
+func cmdclean() {
+	xflagparse(0)
+	clean()
 }
 
 // Banner prints the 'now you've installed Go' banner.
-void
-cmdbanner(int argc, char **argv)
-{
-	char *pathsep, *pid, *ns;
-	Buf b, b1, search, path;
-
-	ARGBEGIN{
-	case 'v':
-		vflag++;
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	if(argc > 0)
-		usage();
-
-	binit(&b);
-	binit(&b1);
-	binit(&search);
-	binit(&path);
+func cmdbanner() {
+	xflagparse(0)
 
-	xprintf("\n");
-	xprintf("---\n");
-	xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot);
-	xprintf("Installed commands in %s\n", gobin);
+	xprintf("\n")
+	xprintf("---\n")
+	xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
+	xprintf("Installed commands in %s\n", gobin)
 
-	if(!xsamefile(goroot_final, goroot)) {
+	if !xsamefile(goroot_final, goroot) {
 		// If the files are to be moved, don't check that gobin
 		// is on PATH; assume they know what they are doing.
-	} else if(streq(gohostos, "plan9")) {
+	} else if gohostos == "plan9" {
 		// Check that gobin is bound before /bin.
-		readfile(&b, "#c/pid");
-		bsubst(&b, " ", "");
-		pid = btake(&b);
-		bprintf(&b, "/proc/%s/ns", pid);
-		ns = btake(&b);
-		readfile(&b, ns);
-		bprintf(&search, "bind -b %s /bin\n", gobin);
-		if(xstrstr(bstr(&b), bstr(&search)) == nil)
-			xprintf("*** You need to bind %s before /bin.\n", gobin);
+		pid := strings.Replace(readfile("#c/pid"), " ", "", -1)
+		ns := fmt.Sprintf("/proc/%s/ns", pid)
+		if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) {
+			xprintf("*** You need to bind %s before /bin.\n", gobin)
+		}
 	} else {
 		// 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")) {
-		if(isfile(bpathf(&path, "%s/cov", tooldir)))
-			xprintf("\n"
-				"On OS X the debuggers must be installed setgid procmod.\n"
-				"Read and run ./sudo.bash to install the debuggers.\n");
+		pathsep := ":"
+		if gohostos == "windows" {
+			pathsep = ";"
+		}
+		if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) {
+			xprintf("*** You need to add %s to your PATH.\n", gobin)
+		}
 	}
 
-	if(!xsamefile(goroot_final, goroot)) {
-		xprintf("\n"
+	if !xsamefile(goroot_final, goroot) {
+		xprintf("\n"+
 			"The binaries expect %s to be copied or moved to %s\n",
-			goroot, goroot_final);
+			goroot, goroot_final)
 	}
-
-	bfree(&b);
-	bfree(&b1);
-	bfree(&search);
-	bfree(&path);
 }
 
 // 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", goversion);
+func cmdversion() {
+	xflagparse(0)
+	xprintf("%s\n", goversion)
 }
diff --git a/src/cmd/dist/buildgc.go b/src/cmd/dist/buildgc.go
index 64434d51e16852112ee26aa63a503a0b3fa7bdaf..b1b5d5e7ba5869428fddff627a36378debf7d417 100644
--- a/src/cmd/dist/buildgc.go
+++ b/src/cmd/dist/buildgc.go
@@ -2,7 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"strconv"
+	"strings"
+)
 
 /*
  * Helpers for building cmd/gc.
@@ -12,207 +19,152 @@
 // It finds the OXXX enum, pulls out all the constants
 // from OXXX to OEND, and writes a table mapping
 // op to string.
-void
-gcopnames(char *dir, char *file)
-{
-	char *p, *q;
-	int i, j, end;
-	Buf in, b, out;
-	Vec lines, fields;
-	
-	binit(&in);
-	binit(&b);
-	binit(&out);
-	vinit(&lines);
-	vinit(&fields);
-	
-	bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n"));
-	bwritestr(&out, bprintf(&b, "static char *opnames[] = {\n"));
-
-	readfile(&in, bprintf(&b, "%s/go.h", dir));
-	splitlines(&lines, bstr(&in));
-	i = 0;
-	while(i<lines.len && !contains(lines.p[i], "OXXX"))
-		i++;
-	end = 0;
-	for(; i<lines.len && !end; i++) {
-		p = xstrstr(lines.p[i], "//");
-		if(p != nil)
-			*p = '\0';
-		end = contains(lines.p[i], "OEND");
-		splitfields(&fields, lines.p[i]);
-		for(j=0; j<fields.len; j++) {
-			q = fields.p[j];
-			if(*q == 'O')
-				q++;
-			p = q+xstrlen(q)-1;
-			if(*p == ',')
-				*p = '\0';
-			bwritestr(&out, bprintf(&b, "	[O%s] = \"%s\",\n", q, q));
+func gcopnames(dir, file string) {
+	var out bytes.Buffer
+	fmt.Fprintf(&out, "// auto generated by go tool dist\n")
+	fmt.Fprintf(&out, "static char *opnames[] = {\n")
+
+	in := readfile(pathf("%s/go.h", dir))
+	lines := splitlines(in)
+	i := 0
+	for i < len(lines) && !strings.Contains(lines[i], "OXXX") {
+		i++
+	}
+	for _, line := range lines[i:] {
+		if i := strings.Index(line, "//"); i >= 0 {
+			line = line[:i]
+		}
+		for _, field := range splitfields(line) {
+			field = strings.TrimPrefix(field, "O")
+			field = strings.TrimSuffix(field, ",")
+			fmt.Fprintf(&out, "\t[O%s] = \"%s\",\n", field, field)
+		}
+		if strings.Contains(line, "OEND") {
+			break
 		}
 	}
-	
-	bwritestr(&out, bprintf(&b, "};\n"));
-
-	writefile(&out, file, 0);
+	fmt.Fprintf(&out, "};\n")
 
-	bfree(&in);
-	bfree(&b);
-	bfree(&out);
-	vfree(&lines);
-	vfree(&fields);
-}
-
-static int
-xatoi(char *s, char **end)
-{
-	int val = 0;
-	for(; *s && *s >= '0' && *s <= '9'; ++s)
-		val = val * 10 + (*s - '0');
-	*end = s;
-	return val;
+	writefile(out.String(), file, 0)
 }
 
 // mkanames reads [5689].out.h and writes anames[5689].c
 // The format is much the same as the Go opcodes above.
 // It also writes out cnames array for C_* constants and the dnames
 // array for D_* constants.
-void
-mkanames(char *dir, char *file)
-{
-	int i, j, ch, n, unknown;
-	Buf in, b, out, out2;
-	Vec lines;
-	char *p, *p2;
-	Vec dnames[128];
-
-	binit(&b);
-	binit(&in);
-	binit(&out);
-	binit(&out2);
-	vinit(&lines);
-	for(i=0; i<nelem(dnames); i++)
-		vinit(&dnames[i]);
-
-	ch = file[xstrlen(file)-3];
-	bprintf(&b, "%s/../cmd/%cl/%c.out.h", dir, ch, ch);
-	readfile(&in, bstr(&b));
-	splitlines(&lines, bstr(&in));
-	
+func mkanames(dir, file string) {
+	ch := file[len(file)-3]
+	targ := pathf("%s/../cmd/%cl/%c.out.h", dir, ch, ch)
+	in := readfile(targ)
+	lines := splitlines(in)
+
 	// Include link.h so that the extern declaration there is
 	// checked against the non-extern declaration we are generating.
-	bwritestr(&out, bprintf(&b, "// auto generated by go tool dist\n"));
-	bwritestr(&out, bprintf(&b, "#include <u.h>\n"));
-	bwritestr(&out, bprintf(&b, "#include <libc.h>\n"));
-	bwritestr(&out, bprintf(&b, "#include <bio.h>\n"));
-	bwritestr(&out, bprintf(&b, "#include <link.h>\n"));
-	bwritestr(&out, bprintf(&b, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch));
-	bwritestr(&out, bprintf(&b, "\n"));
-
-	bwritestr(&out, bprintf(&b, "char*	anames%c[] = {\n", ch));
-	for(i=0; i<lines.len; i++) {
-		if(hasprefix(lines.p[i], "\tA")) {
-			p = xstrstr(lines.p[i], ",");
-			if(p)
-				*p = '\0';
-			p = xstrstr(lines.p[i], "\n");
-			if(p)
-				*p = '\0';
-			p = lines.p[i] + 2;
-			bwritestr(&out, bprintf(&b, "\t\"%s\",\n", p));
+	var out bytes.Buffer
+	fmt.Fprintf(&out, "// auto generated by go tool dist\n")
+	fmt.Fprintf(&out, "#include <u.h>\n")
+	fmt.Fprintf(&out, "#include <libc.h>\n")
+	fmt.Fprintf(&out, "#include <bio.h>\n")
+	fmt.Fprintf(&out, "#include <link.h>\n")
+	fmt.Fprintf(&out, "#include \"../cmd/%cl/%c.out.h\"\n", ch, ch)
+	fmt.Fprintf(&out, "\n")
+
+	fmt.Fprintf(&out, "char*	anames%c[] = {\n", ch)
+	for _, line := range lines {
+		if strings.HasPrefix(line, "\tA") {
+			if i := strings.Index(line, ","); i >= 0 {
+				line = line[:i]
+			}
+			if i := strings.Index(line, "\n"); i >= 0 {
+				line = line[:i]
+			}
+			line = line[2:]
+			fmt.Fprintf(&out, "\t\"%s\",\n", line)
 		}
 	}
-	bwritestr(&out, "};\n");
-
-	j=0;
-	bprintf(&out2, "char*	cnames%c[] = {\n", ch);
-	for(i=0; i<lines.len; i++) {
-		if(hasprefix(lines.p[i], "\tC_")) {
-			p = xstrstr(lines.p[i], ",");
-			if(p)
-				*p = '\0';
-			p = xstrstr(lines.p[i], "\n");
-			if(p)
-				*p = '\0';
-			p = lines.p[i] + 3;
-			bwritestr(&out2, bprintf(&b, "\t\"%s\",\n", p));
-			j++;
+	fmt.Fprintf(&out, "};\n")
+
+	j := 0
+	var out2 bytes.Buffer
+	fmt.Fprintf(&out2, "char*	cnames%c[] = {\n", ch)
+	for _, line := range lines {
+		if strings.HasPrefix(line, "\tC_") {
+			if i := strings.Index(line, ","); i >= 0 {
+				line = line[:i]
+			}
+			if i := strings.Index(line, "\n"); i >= 0 {
+				line = line[:i]
+			}
+			line = line[3:]
+			fmt.Fprintf(&out2, "\t\"%s\",\n", line)
+			j++
 		}
 	}
-	bwritestr(&out2, "};\n");
-	if(j>0)
-		bwriteb(&out, &out2);
-
-	j=unknown=0;
-	n=-1;
-	for(i=0; i<lines.len; i++) {
-		if(hasprefix(lines.p[i], "\tD_")) {
-			p = xstrstr(lines.p[i], ",");
-			if(p)
-				*p = '\0';
-			p = xstrstr(lines.p[i], "\n");
-			if(p)
-				*p = '\0';
+	fmt.Fprintf(&out2, "};\n")
+	if j > 0 {
+		out.Write(out2.Bytes())
+	}
+
+	var dnames [128][]string
+	j = 0
+	unknown := false
+	n := -1
+	for _, line := range lines {
+		if strings.HasPrefix(line, "\tD_") {
+			if i := strings.Index(line, ","); i >= 0 {
+				line = line[:i]
+			}
 
 			// Parse explicit value, if any
-			p = xstrstr(lines.p[i], "=");
-			if(p) {
-				// Skip space after '='
-				p2 = p + 1;
-				while(*p2 == ' ' || *p2 == '\t')
-					p2++;
-				n = xatoi(p2, &p2);
-				// We can't do anything about
-				// non-numeric values or anything that
-				// follows
-				while(*p2 == ' ' || *p2 == '\t')
-					p2++;
-				if(*p2 != 0) {
-					unknown = 1;
-					continue;
+			if i := strings.Index(line, "="); i >= 0 {
+				value := strings.TrimSpace(line[i+1:])
+				line = strings.TrimSpace(line[:i])
+				var err error
+				n, err = strconv.Atoi(value)
+				if err != nil {
+					// We can't do anything about
+					// non-numeric values or anything that
+					// follows.
+					unknown = true
+					continue
 				}
-				// Truncate space before '='
-				while(*(p-1) == ' ' || *(p-1) == '\t')
-					p--;
-				*p = '\0';
-				unknown = 0;
+				unknown = false
 			} else {
-				n++;
+				n++
+			}
+
+			if unknown || n < 0 || n >= len(dnames) {
+				continue
 			}
 
-			if(unknown || n >= nelem(dnames))
-				continue;
+			line = strings.TrimSpace(line)
+			line = line[len("D_"):]
 
-			p = lines.p[i] + 3;
-			if(xstrcmp(p, "LAST") == 0)
-				continue;
-			vadd(&dnames[n], p);
-			j++;
+			if strings.Contains(line, "LAST") {
+				continue
+			}
+			dnames[n] = append(dnames[n], line)
+			j++
 		}
 	}
-	if(j>0){
-		bwritestr(&out, bprintf(&b, "char*	dnames%c[D_LAST] = {\n", ch));
-		for(i=0; i<nelem(dnames); i++) {
-			if(dnames[i].len == 0)
-				continue;
-			bwritestr(&out, bprintf(&b, "\t[D_%s] = \"", dnames[i].p[0]));
-			for(j=0; j<dnames[i].len; j++) {
-				if(j != 0)
-					bwritestr(&out, "/");
-				bwritestr(&out, dnames[i].p[j]);
+
+	if j > 0 {
+		fmt.Fprintf(&out, "char*	dnames%c[D_LAST] = {\n", ch)
+		for _, d := range dnames {
+			if len(d) == 0 {
+				continue
 			}
-			bwritestr(&out, "\",\n");
+			fmt.Fprintf(&out, "\t[D_%s] = \"", d[0])
+			for k, name := range d {
+				if k > 0 {
+					fmt.Fprintf(&out, "/")
+				}
+				fmt.Fprintf(&out, "%s", name)
+			}
+			fmt.Fprintf(&out, "\",\n")
 		}
-		bwritestr(&out, "};\n");
+		fmt.Fprintf(&out, "};\n")
 	}
 
-	writefile(&out, file, 0);
-
-	bfree(&b);
-	bfree(&in);
-	bfree(&out);
-	bfree(&out2);
-	vfree(&lines);
-	for(i=0; i<nelem(dnames); i++)
-		vfree(&dnames[i]);
+	writefile(out.String(), file, 0)
 }
diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go
index 41208fac5f734d3e3078d868a77a5c88d11fbdcc..9cc650840d4bb0ae48877038ca502ee65ab07548 100644
--- a/src/cmd/dist/buildgo.go
+++ b/src/cmd/dist/buildgo.go
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
+
+import "fmt"
 
 /*
  * Helpers for building cmd/go and cmd/cgo.
@@ -15,35 +17,23 @@
 //	const defaultCXX = <defaultcxx>
 //
 // It is invoked to write cmd/go/zdefaultcc.go
-// but we also write cmd/cgo/zdefaultcc.go.
-void
-mkzdefaultcc(char *dir, char *file)
-{
-	Buf b, out;
-	
-	USED(dir);
-
-	binit(&out);
-	bprintf(&out,
-		"// auto generated by go tool dist\n"
-		"\n"
-		"package main\n"
-		"\n"
-		"const defaultCC = `%s`\n"
-		"const defaultCXX = `%s`\n",
-		defaultcctarget, defaultcxxtarget);
+// but we also write cmd/cgo/zdefaultcc.go
+func mkzdefaultcc(dir, file string) {
+	var out string
 
-	writefile(&out, file, 0);
+	out = fmt.Sprintf(
+		"// auto generated by go tool dist\n"+
+			"\n"+
+			"package main\n"+
+			"\n"+
+			"const defaultCC = `%s`\n"+
+			"const defaultCXX = `%s`\n",
+		defaultcctarget, defaultcxxtarget)
 
-	// Convert file name to replace.
-	binit(&b);	
-	bwritestr(&b, file);
-	if(slash[0] == '/')
-		bsubst(&b, "/go/zdefaultcc.go", "/cgo/zdefaultcc.go");
-	else
-		bsubst(&b, "\\go\\zdefaultcc.go", "\\cgo\\zdefaultcc.go");
-	writefile(&out, bstr(&b), 0);
+	writefile(out, file, 0)
 
-	bfree(&b);
-	bfree(&out);
+	// Convert file name to replace: turn go into cgo.
+	i := len(file) - len("go/zdefaultcc.go")
+	file = file[:i] + "c" + file[i:]
+	writefile(out, file, 0)
 }
diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go
index add68976825a7388fdaafab234ead55d09195c86..c0ec2efbd6cae94f00f906d96798b2cc115ffae0 100644
--- a/src/cmd/dist/buildruntime.go
+++ b/src/cmd/dist/buildruntime.go
@@ -2,7 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
+
+import (
+	"fmt"
+	"os"
+)
 
 /*
  * Helpers for building runtime.
@@ -14,55 +19,28 @@
 //	const defaultGoroot = <goroot>
 //	const theVersion = <version>
 //
-void
-mkzversion(char *dir, char *file)
-{
-	Buf b, out;
-	
-	USED(dir);
-
-	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"
-		"var buildVersion = theVersion\n", goroot_final, goversion));
-
-	writefile(&out, file, 0);
-	
-	bfree(&b);
-	bfree(&out);
+func mkzversion(dir, file string) {
+	out := fmt.Sprintf(
+		"// auto generated by go tool dist\n"+
+			"\n"+
+			"package runtime\n"+
+			"\n"+
+			"const defaultGoroot = `%s`\n"+
+			"const theVersion = `%s`\n"+
+			"var buildVersion = theVersion\n", goroot_final, goversion)
+
+	writefile(out, file, 0)
 }
 
 // mkzexperiment writes zaexperiment.h (sic):
 //
 //	#define GOEXPERIMENT "experiment string"
 //
-void
-mkzexperiment(char *dir, char *file)
-{
-	Buf b, out, exp;
-	
-	USED(dir);
-
-	binit(&b);
-	binit(&out);
-	binit(&exp);
-	
-	xgetenv(&exp, "GOEXPERIMENT");
-	bwritestr(&out, bprintf(&b,
-		"// auto generated by go tool dist\n"
-		"\n"
-		"#define GOEXPERIMENT \"%s\"\n", bstr(&exp)));
+func mkzexperiment(dir, file string) {
+	out := fmt.Sprintf(
+		"// auto generated by go tool dist\n"+
+			"\n"+
+			"#define GOEXPERIMENT \"%s\"\n", os.Getenv("GOEXPERIMENT"))
 
-	writefile(&out, file, 0);
-	
-	bfree(&b);
-	bfree(&out);
-	bfree(&exp);
+	writefile(out, file, 0)
 }
diff --git a/src/cmd/dist/cpuid_386.s b/src/cmd/dist/cpuid_386.s
new file mode 100644
index 0000000000000000000000000000000000000000..853824a1bce65c44b3cef7f7383dd10438d3528c
--- /dev/null
+++ b/src/cmd/dist/cpuid_386.s
@@ -0,0 +1,14 @@
+// Copyright 2015 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.
+
+TEXT ·cpuid(SB),$0-8
+	MOVL ax+4(FP), AX
+	CPUID
+	MOVL info+0(FP), DI
+	MOVL AX, 0(DI)
+	MOVL BX, 4(DI)
+	MOVL CX, 8(DI)
+	MOVL DX, 12(DI)
+	RET
+
diff --git a/src/cmd/dist/cpuid_amd64.s b/src/cmd/dist/cpuid_amd64.s
new file mode 100644
index 0000000000000000000000000000000000000000..dbb1085e896308b3e5c83a739b595154246869f6
--- /dev/null
+++ b/src/cmd/dist/cpuid_amd64.s
@@ -0,0 +1,14 @@
+// Copyright 2015 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.
+
+TEXT ·cpuid(SB),$0-12
+	MOVL ax+8(FP), AX
+	CPUID
+	MOVQ info+0(FP), DI
+	MOVL AX, 0(DI)
+	MOVL BX, 4(DI)
+	MOVL CX, 8(DI)
+	MOVL DX, 12(DI)
+	RET
+
diff --git a/src/cmd/dist/cpuid_default.s b/src/cmd/dist/cpuid_default.s
new file mode 100644
index 0000000000000000000000000000000000000000..e5bfd183d91acb84feb68fed0b2a6d76ff72dc15
--- /dev/null
+++ b/src/cmd/dist/cpuid_default.s
@@ -0,0 +1,10 @@
+// Copyright 2015 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.
+
+// +build !386,!amd64
+
+#include "textflag.h"
+
+TEXT ·cpuid(SB),NOSPLIT,$0-0
+	RET
diff --git a/src/cmd/dist/main.go b/src/cmd/dist/main.go
index fad01802a5952b6c2456d8d3b848dea4757b1fd5..a2ac65ee87ffb70b13dd23f89c9dd6ffca65e9bc 100644
--- a/src/cmd/dist/main.go
+++ b/src/cmd/dist/main.go
@@ -2,41 +2,84 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-#include "a.h"
+package main
 
-int vflag;
-int sflag;
-char *argv0;
+import (
+	"flag"
+	"fmt"
+	"os"
+	"strconv"
+)
 
 // cmdtab records the available commands.
-static struct {
-	char *name;
-	void (*f)(int, char**);
-} cmdtab[] = {
+var cmdtab = []struct {
+	name string
+	f    func()
+}{
 	{"banner", cmdbanner},
 	{"bootstrap", cmdbootstrap},
 	{"clean", cmdclean},
 	{"env", cmdenv},
 	{"install", cmdinstall},
 	{"version", cmdversion},
-};
+}
 
 // The OS-specific main calls into the portable code here.
-void
-xmain(int argc, char **argv)
-{
-	int i;
-
-	if(argc <= 1)
-		usage();
-	
-	for(i=0; i<nelem(cmdtab); i++) {
-		if(streq(cmdtab[i].name, argv[1])) {
-			cmdtab[i].f(argc-1, argv+1);
-			return;
+func xmain() {
+	if len(os.Args) < 2 {
+		usage()
+	}
+	cmd := os.Args[1]
+	os.Args = os.Args[1:] // for flag parsing during cmd
+	for _, ct := range cmdtab {
+		if ct.name == cmd {
+			flag.Usage = func() {
+				fmt.Fprintf(os.Stderr, "usage: go tool dist %s [options]\n", cmd)
+				flag.PrintDefaults()
+				os.Exit(2)
+			}
+			ct.f()
+			return
+		}
+	}
+
+	xprintf("unknown command %s\n", cmd)
+	usage()
+}
+
+func xflagparse(maxargs int) {
+	flag.Var((*count)(&vflag), "v", "verbosity")
+	flag.Parse()
+	if maxargs >= 0 && flag.NArg() > maxargs {
+		flag.Usage()
+	}
+}
+
+// count is a flag.Value that is like a flag.Bool and a flag.Int.
+// If used as -name, it increments the count, but -name=x sets the count.
+// Used for verbose flag -v.
+type count int
+
+func (c *count) String() string {
+	return fmt.Sprint(int(*c))
+}
+
+func (c *count) Set(s string) error {
+	switch s {
+	case "true":
+		*c++
+	case "false":
+		*c = 0
+	default:
+		n, err := strconv.Atoi(s)
+		if err != nil {
+			return fmt.Errorf("invalid count %q", s)
 		}
+		*c = count(n)
 	}
+	return nil
+}
 
-	xprintf("unknown command %s\n", argv[1]);
-	usage();
+func (c *count) IsBoolFlag() bool {
+	return true
 }
diff --git a/src/cmd/dist/sys_default.go b/src/cmd/dist/sys_default.go
new file mode 100644
index 0000000000000000000000000000000000000000..ab97f19b3d9e2c1693b0df01ab56fc9242b1a622
--- /dev/null
+++ b/src/cmd/dist/sys_default.go
@@ -0,0 +1,10 @@
+// Copyright 2015 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.
+
+// +build !windows,!plan9
+
+package main
+
+func sysinit() {
+}
diff --git a/src/cmd/dist/sys_windows.go b/src/cmd/dist/sys_windows.go
new file mode 100644
index 0000000000000000000000000000000000000000..c6867fb8954d17dc414fa9550bc26035dd50a80b
--- /dev/null
+++ b/src/cmd/dist/sys_windows.go
@@ -0,0 +1,49 @@
+// Copyright 2015 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.
+
+package main
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+var (
+	modkernel32       = syscall.NewLazyDLL("kernel32.dll")
+	procGetSystemInfo = syscall.NewProc("GetSystemInfo")
+)
+
+// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms724958(v=vs.85).aspx
+type systeminfo struct {
+	wProcessorArchitecture      uint16
+	wReserved                   uint16
+	dwPageSize                  uint32
+	lpMinimumApplicationAddress uintptr
+	lpMaximumApplicationAddress uintptr
+	dwActiveProcessorMask       uintptr
+	dwNumberOfProcessors        uint32
+	dwProcessorType             uint32
+	dwAllocationGranularity     uint32
+	wProcessorLevel             uint16
+	wProcessorRevision          uint16
+}
+
+const (
+	PROCESSOR_ARCHITECTURE_AMD64 = 9
+	PROCESSOR_ARCHITECTURE_INTEL = 0
+)
+
+var sysinfo systeminfo
+
+func sysinit() {
+	syscall.Syscall(procGetSystemInfo.Addr(), 1, uintptr(unsafe.Pointer(&sysinfo)), 0, 0)
+	switch sysinfo.wProcessorArchitecture {
+	case PROCESSOR_ARCHITECTURE_AMD64:
+		gohostarch = "amd64"
+	case PROCESSOR_ARCHITECTURE_INTEL:
+		gohostarch = "386"
+	default:
+		fatal("unknown processor architecture")
+	}
+}
diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go
index 0fd17c15091461152d60929f0566cd1447c2ca59..4628eead80fe2f4c5a2c8ac1b600f5a922254606 100644
--- a/src/cmd/dist/util.go
+++ b/src/cmd/dist/util.go
@@ -2,722 +2,375 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// These #ifdefs are being used as a substitute for
-// build configuration, so that on any system, this
-// tool can be built with the local equivalent of
-//	cc *.c
-//
-#ifndef WIN32
-#ifndef PLAN9
-
-#include "a.h"
-#include <unistd.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <sys/param.h>
-#include <sys/utsname.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <setjmp.h>
-#include <signal.h>
-
-// bprintf replaces the buffer with the result of the printf formatting
-// and returns a pointer to the NUL-terminated buffer contents.
-char*
-bprintf(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);
-}
-
-// 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)
-{
-	int n;
-
-	for(;;) {
-		bgrow(b, 4096);
-		n = read(fd, b->p+b->len, 4096);
-		if(n < 0)
-			fatal("read: %s", strerror(errno));
-		if(n == 0)
-			break;
-		b->len += n;
-	}
-}
-
-// xgetenv replaces b with the value of the named environment variable.
-void
-xgetenv(Buf *b, char *name)
-{
-	char *p;
-	
-	breset(b);
-	p = getenv(name);
-	if(p != NULL)
-		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.
-// If mode is CheckExit, run calls fatal if the command is not successful.
-void
-run(Buf *b, char *dir, int mode, char *cmd, ...)
-{
-	va_list arg;
-	Vec argv;
-	char *p;
-	
-	vinit(&argv);
-	vadd(&argv, cmd);
-	va_start(arg, cmd);
-	while((p = va_arg(arg, char*)) != nil)
-		vadd(&argv, p);
-	va_end(arg);
-	
-	runv(b, dir, mode, &argv);
-	
-	vfree(&argv);
-}
-
-// runv is like run but takes a vector.
-void
-runv(Buf *b, char *dir, int mode, Vec *argv)
-{
-	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;
-	Buf *b;
-} bg[MAXBG];
-static int nbg;
-static int maxnbg = nelem(bg);
-
-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 >= maxnbg)
-		bgwait1();
-
-	// Generate a copy of the command to show in a log.
-	// Substitute $WORK for the work directory.
-	binit(&cmd);
-	for(i=0; i<argv->len; i++) {
-		if(i > 0)
-			bwritestr(&cmd, " ");
-		q = argv->p[i];
-		if(workdir != nil && hasprefix(q, workdir)) {
-			bwritestr(&cmd, "$WORK");
-			q += strlen(workdir);
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+// pathf is fmt.Sprintf for generating paths
+// (on windows it turns / into \ after the printf).
+func pathf(format string, args ...interface{}) string {
+	return filepath.Clean(fmt.Sprintf(format, args...))
+}
+
+// filter returns a slice containing the elements x from list for which f(x) == true.
+func filter(list []string, f func(string) bool) []string {
+	var out []string
+	for _, x := range list {
+		if f(x) {
+			out = append(out, x)
 		}
-		bwritestr(&cmd, q);
-	}
-	if(vflag > 1)
-		errprintf("%s\n", bstr(&cmd));
-
-	if(b != nil) {
-		breset(b);
-		if(pipe(p) < 0)
-			fatal("pipe: %s", strerror(errno));
-	}
-
-	switch(pid = fork()) {
-	case -1:
-		fatal("fork: %s", strerror(errno));
-	case 0:
-		if(b != nil) {
-			close(0);
-			close(p[0]);
-			dup2(p[1], 1);
-			dup2(p[1], 2);
-			if(p[1] > 2)
-				close(p[1]);
+	}
+	return out
+}
+
+// uniq returns a sorted slice containing the unique elements of list.
+func uniq(list []string) []string {
+	out := make([]string, len(list))
+	copy(out, list)
+	sort.Strings(out)
+	keep := out[:0]
+	for _, x := range out {
+		if len(keep) == 0 || keep[len(keep)-1] != x {
+			keep = append(keep, x)
+		}
+	}
+	return keep
+}
+
+// splitlines returns a slice with the result of splitting
+// the input p after each \n.
+func splitlines(p string) []string {
+	return strings.SplitAfter(p, "\n")
+}
+
+// splitfields replaces the vector v with the result of splitting
+// the input p into non-empty fields containing no spaces.
+func splitfields(p string) []string {
+	return strings.Fields(p)
+}
+
+const (
+	CheckExit = 1 << iota
+	ShowOutput
+	Background
+)
+
+var outputLock sync.Mutex
+
+// run runs the command line cmd in dir.
+// If mode has ShowOutput set, run collects cmd's output and returns it as a string;
+// otherwise, run prints cmd's output to standard output after the command finishes.
+// If mode has CheckExit set and the command fails, run calls fatal.
+// If mode has Background set, this command is being run as a
+// Background job. Only bgrun should use the Background mode,
+// not other callers.
+func run(dir string, mode int, cmd ...string) string {
+	if vflag > 1 {
+		errprintf("run: %s\n", strings.Join(cmd, " "))
+	}
+
+	xcmd := exec.Command(cmd[0], cmd[1:]...)
+	xcmd.Dir = dir
+	var err error
+	data, err := xcmd.CombinedOutput()
+	if err != nil && mode&CheckExit != 0 {
+		outputLock.Lock()
+		if len(data) > 0 {
+			xprintf("%s\n", data)
+		}
+		outputLock.Unlock()
+		atomic.AddInt32(&ndone, +1)
+		die := func() {
+			time.Sleep(100 * time.Millisecond)
+			fatal("FAILED: %v", strings.Join(cmd, " "))
 		}
-		if(dir != nil) {
-			if(chdir(dir) < 0) {
-				fprintf(stderr, "chdir %s: %s\n", dir, strerror(errno));
-				_exit(1);
-			}
+		if mode&Background != 0 {
+			// This is a background run, and fatal will
+			// wait for it to finish before exiting.
+			// If we call fatal directly, that's a deadlock.
+			// Instead, call fatal in a background goroutine
+			// and let this run return normally, so that
+			// fatal can wait for it to finish.
+			go die()
+		} else {
+			die()
 		}
-		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);
-	}
-	if(b != nil) {
-		close(p[1]);
-		breadfrom(b, p[0]);
-		close(p[0]);
-	}
-
-	if(nbg < 0)
-		fatal("bad bookkeeping");
-	bg[nbg].pid = pid;
-	bg[nbg].mode = mode;
-	bg[nbg].cmd = btake(&cmd);
-	bg[nbg].b = b;
-	nbg++;
-	
-	if(wait)
-		bgwait();
-
-	bfree(&cmd);
-}
-
-// bgwait1 waits for a single background job.
-static void
-bgwait1(void)
-{
-	int i, pid, status, mode;
-	char *cmd;
-	Buf *b;
-
-	errno = 0;
-	while((pid = wait(&status)) < 0) {
-		if(errno != EINTR)
-			fatal("waitpid: %s", strerror(errno));
-	}
-	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;
-	b = bg[i].b;
-	bg[i].b = nil;
-	bg[i] = bg[--nbg];
-	
-	if(mode == CheckExit && (!WIFEXITED(status) || WEXITSTATUS(status) != 0)) {
-		if(b != nil)
-			xprintf("%s\n", bstr(b));
-		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.
-void
-xgetwd(Buf *b)
-{
-	char buf[MAXPATHLEN];
-	
-	breset(b);
-	if(getcwd(buf, MAXPATHLEN) == nil)
-		fatal("getcwd: %s", strerror(errno));
-	bwritestr(b, buf);	
-}
-
-// xrealwd replaces b with the 'real' name for the given path.
-// real is defined as what getcwd returns in that directory.
-void
-xrealwd(Buf *b, char *path)
-{
-	int fd;
-	
-	fd = open(".", 0);
-	if(fd < 0)
-		fatal("open .: %s", strerror(errno));
-	if(chdir(path) < 0)
-		fatal("chdir %s: %s", path, strerror(errno));
-	xgetwd(b);
-	if(fchdir(fd) < 0)
-		fatal("fchdir: %s", strerror(errno));
-	close(fd);
+	}
+	if mode&ShowOutput != 0 {
+		os.Stdout.Write(data)
+	}
+	return string(data)
+}
+
+var maxbg = 4 /* maximum number of jobs to run at once */
+
+var (
+	bgwork = make(chan func())
+	bgdone = make(chan struct{}, 1e6)
+	nwork  int32
+	ndone  int32
+)
+
+func bginit() {
+	for i := 0; i < maxbg; i++ {
+		go bghelper()
+	}
+}
+
+func bghelper() {
+	for {
+		(<-bgwork)()
+	}
+}
+
+// bgrun is like run but runs the command in the background.
+// CheckExit|ShowOutput mode is implied (since output cannot be returned).
+func bgrun(dir string, cmd ...string) {
+	bgwork <- func() {
+		run(dir, CheckExit|ShowOutput|Background, cmd...)
+	}
+}
+
+// bgwait waits for pending bgruns to finish.
+func bgwait() {
+	var wg sync.WaitGroup
+	wg.Add(maxbg)
+	for i := 0; i < maxbg; i++ {
+		bgwork <- func() {
+			wg.Done()
+			wg.Wait()
+		}
+	}
+	wg.Wait()
+}
+
+// xgetwd returns the current directory.
+func xgetwd() string {
+	wd, err := os.Getwd()
+	if err != nil {
+		fatal("%s", err)
+	}
+	return wd
+}
+
+// xrealwd returns the 'real' name for the given path.
+// real is defined as what xgetwd returns in that directory.
+func xrealwd(path string) string {
+	old := xgetwd()
+	if err := os.Chdir(path); err != nil {
+		fatal("chdir %s: %v", path, err)
+	}
+	real := xgetwd()
+	if err := os.Chdir(old); err != nil {
+		fatal("chdir %s: %v", old, err)
+	}
+	return real
 }
 
 // isdir reports whether p names an existing directory.
-bool
-isdir(char *p)
-{
-	struct stat st;
-	
-	return stat(p, &st) >= 0 && S_ISDIR(st.st_mode);
+func isdir(p string) bool {
+	fi, err := os.Stat(p)
+	return err == nil && fi.IsDir()
 }
 
 // isfile reports whether p names an existing file.
-bool
-isfile(char *p)
-{
-	struct stat st;
-	
-	return stat(p, &st) >= 0 && S_ISREG(st.st_mode);
+func isfile(p string) bool {
+	fi, err := os.Stat(p)
+	return err == nil && fi.Mode().IsRegular()
 }
 
 // mtime returns the modification time of the file p.
-Time
-mtime(char *p)
-{
-	struct stat st;
-	
-	if(stat(p, &st) < 0)
-		return 0;
-	return (Time)st.st_mtime*1000000000LL;
+func mtime(p string) time.Time {
+	fi, err := os.Stat(p)
+	if err != nil {
+		return time.Time{}
+	}
+	return fi.ModTime()
 }
 
 // isabs reports whether p is an absolute path.
-bool
-isabs(char *p)
-{
-	return hasprefix(p, "/");
-}
-
-// readfile replaces b with the content of the named file.
-void
-readfile(Buf *b, char *file)
-{
-	int fd;
-	
-	breset(b);
-	fd = open(file, 0);
-	if(fd < 0)
-		fatal("open %s: %s", file, strerror(errno));
-	breadfrom(b, fd);
-	close(fd);
+func isabs(p string) bool {
+	return filepath.IsAbs(p)
+}
+
+// readfile returns the content of the named file.
+func readfile(file string) string {
+	data, err := ioutil.ReadFile(file)
+	if err != nil {
+		fatal("%v", err)
+	}
+	return string(data)
 }
 
 // writefile writes b to the named file, creating it if needed.  if
 // exec is non-zero, marks the file as executable.
-void
-writefile(Buf *b, char *file, int exec)
-{
-	int fd;
-	
-	fd = creat(file, 0666);
-	if(fd < 0)
-		fatal("create %s: %s", file, strerror(errno));
-	if(write(fd, b->p, b->len) != b->len)
-		fatal("short write: %s", strerror(errno));
-	if(exec)
-		fchmod(fd, 0755);
-	close(fd);
+func writefile(b, file string, exec int) {
+	mode := os.FileMode(0666)
+	if exec != 0 {
+		mode = 0777
+	}
+	err := ioutil.WriteFile(file, []byte(b), mode)
+	if err != nil {
+		fatal("%v", err)
+	}
 }
 
 // xmkdir creates the directory p.
-void
-xmkdir(char *p)
-{
-	if(mkdir(p, 0777) < 0)
-		fatal("mkdir %s: %s", p, strerror(errno));
+func xmkdir(p string) {
+	err := os.Mkdir(p, 0777)
+	if err != nil {
+		fatal("%v", err)
+	}
 }
 
 // xmkdirall creates the directory p and its parents, as needed.
-void
-xmkdirall(char *p)
-{
-	char *q;
-
-	if(isdir(p))
-		return;
-	q = strrchr(p, '/');
-	if(q != nil) {
-		*q = '\0';
-		xmkdirall(p);
-		*q = '/';
+func xmkdirall(p string) {
+	err := os.MkdirAll(p, 0777)
+	if err != nil {
+		fatal("%v", err)
 	}
-	xmkdir(p);
 }
 
 // xremove removes the file p.
-void
-xremove(char *p)
-{
-	if(vflag > 2)
-		errprintf("rm %s\n", p);
-	unlink(p);
+func xremove(p string) {
+	if vflag > 2 {
+		errprintf("rm %s\n", p)
+	}
+	os.Remove(p)
 }
 
 // xremoveall removes the file or directory tree rooted at p.
-void
-xremoveall(char *p)
-{
-	int i;
-	Buf b;
-	Vec dir;
-
-	binit(&b);
-	vinit(&dir);
-
-	if(isdir(p)) {
-		xreaddir(&dir, p);
-		for(i=0; i<dir.len; i++) {
-			bprintf(&b, "%s/%s", p, dir.p[i]);
-			xremoveall(bstr(&b));
-		}
-		if(vflag > 2)
-			errprintf("rm %s\n", p);
-		rmdir(p);
-	} else {
-		if(vflag > 2)
-			errprintf("rm %s\n", p);
-		unlink(p);
+func xremoveall(p string) {
+	if vflag > 2 {
+		errprintf("rm -r %s\n", p)
 	}
-	
-	bfree(&b);
-	vfree(&dir);
+	os.RemoveAll(p)
 }
 
 // xreaddir replaces dst with a list of the names of the files in dir.
 // The names are relative to dir; they are not full paths.
-void
-xreaddir(Vec *dst, char *dir)
-{
-	DIR *d;
-	struct dirent *dp;
-
-	vreset(dst);
-	d = opendir(dir);
-	if(d == nil)
-		fatal("opendir %s: %s", dir, strerror(errno));
-	while((dp = readdir(d)) != nil) {
-		if(streq(dp->d_name, ".") || streq(dp->d_name, ".."))
-			continue;
-		vadd(dst, dp->d_name);
+func xreaddir(dir string) []string {
+	f, err := os.Open(dir)
+	if err != nil {
+		fatal("%v", err)
 	}
-	closedir(d);
+	defer f.Close()
+	names, err := f.Readdirnames(-1)
+	if err != nil {
+		fatal("reading %s: %v", dir, err)
+	}
+	return names
 }
 
 // xworkdir creates a new temporary directory to hold object files
 // and returns the name of that directory.
-char*
-xworkdir(void)
-{
-	Buf b;
-	char *p;
-
-	binit(&b);
-
-	xgetenv(&b, "TMPDIR");
-	if(b.len == 0)
-		bwritestr(&b, "/var/tmp");
-	if(b.p[b.len-1] != '/')
-		bwrite(&b, "/", 1);
-	bwritestr(&b, "go-cbuild-XXXXXX");
-	p = bstr(&b);
-	if(mkdtemp(p) == nil)
-		fatal("mkdtemp(%s): %s", p, strerror(errno));
-	p = btake(&b);
-
-	bfree(&b);
-
-	return p;
+func xworkdir() string {
+	name, err := ioutil.TempDir("", "go-tool-dist-")
+	if err != nil {
+		fatal("%v", err)
+	}
+	return name
 }
 
 // fatal prints an error message to standard error and exits.
-void
-fatal(char *msg, ...)
-{
-	va_list arg;
-	
-	fflush(stdout);
-	fprintf(stderr, "go tool dist: ");
-	va_start(arg, msg);
-	vfprintf(stderr, msg, arg);
-	va_end(arg);
-	fprintf(stderr, "\n");
-	
-	bgwait();
-	exit(1);
-}
-
-// xmalloc returns a newly allocated zeroed block of n bytes of memory.
-// It calls fatal if it runs out of memory.
-void*
-xmalloc(int n)
-{
-	void *p;
-	
-	p = malloc(n);
-	if(p == nil)
-		fatal("out of memory");
-	memset(p, 0, n);
-	return p;
-}
-
-// xstrdup returns a newly allocated copy of p.
-// It calls fatal if it runs out of memory.
-char*
-xstrdup(char *p)
-{
-	p = strdup(p);
-	if(p == nil)
-		fatal("out of memory");
-	return p;
-}
-
-// xrealloc grows the allocation p to n bytes and
-// returns the new (possibly moved) pointer.
-// It calls fatal if it runs out of memory.
-void*
-xrealloc(void *p, int n)
-{
-	p = realloc(p, n);
-	if(p == nil)
-		fatal("out of memory");
-	return p;
-}
-
-// xfree frees the result returned by xmalloc, xstrdup, or xrealloc.
-void
-xfree(void *p)
-{
-	free(p);
-}
-
-// hassuffix reports whether p ends with suffix.
-bool
-hassuffix(char *p, char *suffix)
-{
-	int np, ns;
-
-	np = strlen(p);
-	ns = strlen(suffix);
-	return np >= ns && streq(p+np-ns, suffix);
-}
-
-// hasprefix reports whether p begins with prefix.
-bool
-hasprefix(char *p, char *prefix)
-{
-	return strncmp(p, prefix, strlen(prefix)) == 0;
-}
-
-// contains reports whether sep appears in p.
-bool
-contains(char *p, char *sep)
-{
-	return strstr(p, sep) != nil;
-}
-
-// streq reports whether p and q are the same string.
-bool
-streq(char *p, char *q)
-{
-	return strcmp(p, q) == 0;
-}
-
-// lastelem returns the final path element in p.
-char*
-lastelem(char *p)
-{
-	char *out;
-
-	out = p;
-	for(; *p; p++)
-		if(*p == '/')
-			out = p+1;
-	return out;
-}
-
-// xmemmove copies n bytes from src to dst.
-void
-xmemmove(void *dst, void *src, int n)
-{
-	memmove(dst, src, n);
-}
-
-// xmemcmp compares the n-byte regions starting at a and at b.
-int
-xmemcmp(void *a, void *b, int n)
-{
-	return memcmp(a, b, n);
-}
-
-// xstrlen returns the length of the NUL-terminated string at p.
-int
-xstrlen(char *p)
-{
-	return strlen(p);
+func fatal(format string, args ...interface{}) {
+	fmt.Fprintf(os.Stderr, "go tool dist: %s\n", fmt.Sprintf(format, args...))
+	bgwait()
+	xexit(2)
 }
 
+var atexits []func()
+
 // xexit exits the process with return code n.
-void
-xexit(int n)
-{
-	exit(n);
+func xexit(n int) {
+	for i := len(atexits) - 1; i >= 0; i-- {
+		atexits[i]()
+	}
+	os.Exit(n)
 }
 
 // xatexit schedules the exit-handler f to be run when the program exits.
-void
-xatexit(void (*f)(void))
-{
-	atexit(f);
+func xatexit(f func()) {
+	atexits = append(atexits, f)
 }
 
 // xprintf prints a message to standard output.
-void
-xprintf(char *fmt, ...)
-{
-	va_list arg;
-	
-	va_start(arg, fmt);
-	vprintf(fmt, arg);
-	va_end(arg);
+func xprintf(format string, args ...interface{}) {
+	fmt.Printf(format, args...)
 }
 
 // errprintf prints a message to standard output.
-void
-errprintf(char *fmt, ...)
-{
-	va_list arg;
-	
-	va_start(arg, fmt);
-	vfprintf(stderr, fmt, arg);
-	va_end(arg);
-}
-
-// xsetenv sets the environment variable $name to the given value.
-void
-xsetenv(char *name, char *value)
-{
-	setenv(name, value, 1);
+func errprintf(format string, args ...interface{}) {
+	fmt.Fprintf(os.Stderr, format, args...)
 }
 
 // main takes care of OS-specific startup and dispatches to xmain.
-int
-main(int argc, char **argv)
-{
-	Buf b;
-	int osx;
-	struct utsname u;
-
-	setvbuf(stdout, nil, _IOLBF, 0);
-	setvbuf(stderr, nil, _IOLBF, 0);
-
-	setenv("TERM", "dumb", 1); // disable escape codes in clang errors
-
-	binit(&b);
-	
-	slash = "/";
-
-#if defined(__APPLE__)
-	gohostos = "darwin";
-	// Even on 64-bit platform, darwin uname -m prints i386.
-	run(&b, nil, 0, "sysctl", "machdep.cpu.extfeatures", nil);
-	if(contains(bstr(&b), "EM64T"))
-		gohostarch = "amd64";
-#elif defined(__linux__)
-	gohostos = "linux";
-#elif defined(__DragonFly__)
-	gohostos = "dragonfly";
-#elif defined(__FreeBSD__)
-	gohostos = "freebsd";
-#elif defined(__FreeBSD_kernel__)
-	// detect debian/kFreeBSD. 
-	// http://wiki.debian.org/Debian_GNU/kFreeBSD_FAQ#Q._How_do_I_detect_kfreebsd_with_preprocessor_directives_in_a_C_program.3F
-	gohostos = "freebsd";	
-#elif defined(__OpenBSD__)
-	gohostos = "openbsd";
-#elif defined(__NetBSD__)
-	gohostos = "netbsd";
-#elif defined(__sun) && defined(__SVR4)
-	gohostos = "solaris";
-	// Even on 64-bit platform, solaris uname -m prints i86pc.
-	run(&b, nil, 0, "isainfo", "-n", nil);
-	if(contains(bstr(&b), "amd64"))
-		gohostarch = "amd64";
-	if(contains(bstr(&b), "i386"))
-		gohostarch = "386";
-#else
-	fatal("unknown operating system");
-#endif
-
-	if(gohostarch == nil) {
-		if(uname(&u) < 0)
-			fatal("uname: %s", strerror(errno));
-		if(contains(u.machine, "x86_64") || contains(u.machine, "amd64"))
-			gohostarch = "amd64";
-		else if(hassuffix(u.machine, "86"))
-			gohostarch = "386";
-		else if(contains(u.machine, "arm"))
-			gohostarch = "arm";
-		else if(contains(u.machine, "ppc64le"))
-			gohostarch = "ppc64le";
-		else if(contains(u.machine, "ppc64"))
-			gohostarch = "ppc64";
-		else
-			fatal("unknown architecture: %s", u.machine);
-	}
-
-	if(streq(gohostarch, "arm"))
-		maxnbg = 1;
+func main() {
+	os.Setenv("TERM", "dumb") // disable escape codes in clang errors
+
+	slash = string(filepath.Separator)
+
+	gohostos = runtime.GOOS
+	switch gohostos {
+	case "darwin":
+		// Even on 64-bit platform, darwin uname -m prints i386.
+		if strings.Contains(run("", CheckExit, "sysctl", "machdep.cpu.extfeatures"), "EM64T") {
+			gohostarch = "amd64"
+		}
+	case "solaris":
+		// Even on 64-bit platform, solaris uname -m prints i86pc.
+		out := run("", CheckExit, "isainfo", "-n")
+		if strings.Contains(out, "amd64") {
+			gohostarch = "amd64"
+		}
+		if strings.Contains(out, "i386") {
+			gohostarch = "386"
+		}
+	case "plan9":
+		gohostarch = os.Getenv("objtype")
+		if gohostarch == "" {
+			fatal("$objtype is unset")
+		}
+	}
+
+	sysinit()
+
+	if gohostarch == "" {
+		// Default Unix system.
+		out := run("", CheckExit, "uname", "-m")
+		switch {
+		case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"):
+			gohostarch = "amd64"
+		case strings.Contains(out, "86"):
+			gohostarch = "386"
+		case strings.Contains(out, "arm"):
+			gohostarch = "arm"
+		case strings.Contains(out, "ppc64le"):
+			gohostarch = "ppc64le"
+		case strings.Contains(out, "ppc64"):
+			gohostarch = "ppc64"
+		default:
+			fatal("unknown architecture: %s", out)
+		}
+	}
+
+	if gohostarch == "arm" {
+		maxbg = 1
+	}
+	bginit()
 
 	// The OS X 10.6 linker does not support external linking mode.
 	// See golang.org/issue/5130.
@@ -728,120 +381,77 @@ main(int argc, char **argv)
 	//
 	// Roughly, OS X 10.N shows up as uname release (N+4),
 	// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
-	if(streq(gohostos, "darwin")) {
-		if(uname(&u) < 0)
-			fatal("uname: %s", strerror(errno));
-		osx = atoi(u.release) - 4;
-		if(osx <= 6)
-			goextlinkenabled = "0";
-		if(osx >= 8)
-			defaultclang = 1;
+	if gohostos == "darwin" {
+		rel := run("", CheckExit, "uname", "-r")
+		if i := strings.Index(rel, "."); i >= 0 {
+			rel = rel[:i]
+		}
+		osx, _ := strconv.Atoi(rel)
+		if osx <= 6+4 {
+			goextlinkenabled = "0"
+		}
+		if osx >= 8+4 {
+			defaultclang = true
+		}
 	}
 
-	init();
-	xmain(argc, argv);
-	bfree(&b);
-	return 0;
+	xinit()
+	xmain()
 }
 
-// xqsort is a wrapper for the C standard qsort.
-void
-xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*))
-{
-	qsort(data, n, elemsize, cmp);
+// xsamefile reports whether f1 and f2 are the same file (or dir)
+func xsamefile(f1, f2 string) bool {
+	fi1, err1 := os.Stat(f1)
+	fi2, err2 := os.Stat(f2)
+	if err1 != nil || err2 != nil {
+		return f1 == f2
+	}
+	return os.SameFile(fi1, fi2)
 }
 
-// xstrcmp compares the NUL-terminated strings a and b.
-int
-xstrcmp(char *a, char *b)
-{
-	return strcmp(a, b);
+func cpuid(info *[4]uint32, ax uint32)
+
+func cansse2() bool {
+	if gohostarch != "386" && gohostarch != "amd64" {
+		return false
+	}
+
+	var info [4]uint32
+	cpuid(&info, 1)
+	return info[3]&(1<<26) != 0 // SSE2
 }
 
-// xstrstr returns a pointer to the first occurrence of b in a.
-char*
-xstrstr(char *a, char *b)
-{
-	return strstr(a, b);
+func xgetgoarm() string {
+	if goos == "nacl" {
+		// NaCl guarantees VFPv3 and is always cross-compiled.
+		return "7"
+	}
+	if gohostarch != "arm" || goos != gohostos {
+		// Conservative default for cross-compilation.
+		return "5"
+	}
+	if goos == "freebsd" {
+		// FreeBSD has broken VFP support.
+		return "5"
+	}
+	if xtryexecfunc(useVFPv3) {
+		return "7"
+	}
+	if xtryexecfunc(useVFPv1) {
+		return "6"
+	}
+	return "5"
 }
 
-// xstrrchr returns a pointer to the final occurrence of c in p.
-char*
-xstrrchr(char *p, int c)
-{
-	return strrchr(p, c);
+func xtryexecfunc(f func()) bool {
+	// TODO(rsc): Implement.
+	// The C cmd/dist used this to test whether certain assembly
+	// sequences could be executed properly. It used signals and
+	// timers and sigsetjmp, which is basically not possible in Go.
+	// We probably have to invoke ourselves as a subprocess instead,
+	// to contain the fault/timeout.
+	return false
 }
 
-// xsamefile reports whether f1 and f2 are the same file (or dir)
-int
-xsamefile(char *f1, char *f2)
-{
-	return streq(f1, f2); // suffice for now
-}
-
-sigjmp_buf sigill_jmpbuf;
-static void sigillhand(int);
-
-// xtryexecfunc tries to execute function f, if any illegal instruction
-// signal received in the course of executing that function, it will
-// return 0, otherwise it will return 1.
-// Some systems (notably NetBSD) will spin and spin when executing VFPv3
-// instructions on VFPv2 system (e.g. Raspberry Pi) without ever triggering
-// SIGILL, so we set a 1-second alarm to catch that case.
-int
-xtryexecfunc(void (*f)(void))
-{
-	int r;
-	r = 0;
-	signal(SIGILL, sigillhand);
-	signal(SIGALRM, sigillhand);
-	alarm(1);
-	if(sigsetjmp(sigill_jmpbuf, 1) == 0) {
-		f();
-		r = 1;
-	}
-	signal(SIGILL, SIG_DFL);
-	alarm(0);
-	signal(SIGALRM, SIG_DFL);
-	return r;
-}
-
-// SIGILL handler helper
-static void
-sigillhand(int signum)
-{
-	USED(signum);
-	siglongjmp(sigill_jmpbuf, 1);
-}
-
-static void
-__cpuid(int dst[4], int ax)
-{
-#ifdef __i386__
-	// we need to avoid ebx on i386 (esp. when -fPIC).
-	asm volatile(
-		"mov %%ebx, %%edi\n\t"
-		"cpuid\n\t"
-		"xchgl %%ebx, %%edi"
-		: "=a" (dst[0]), "=D" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
-		: "0" (ax));
-#elif defined(__x86_64__)
-	asm volatile("cpuid"
-		: "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3])
-		: "0" (ax));
-#else
-	dst[0] = dst[1] = dst[2] = dst[3] = 0;
-#endif
-}
-
-bool
-cansse2(void)
-{
-	int info[4];
-	
-	__cpuid(info, 1);
-	return (info[3] & (1<<26)) != 0;	// SSE2
-}
-
-#endif // PLAN9
-#endif // __WINDOWS__
+func useVFPv1()
+func useVFPv3()
diff --git a/src/cmd/dist/vfp_arm.s b/src/cmd/dist/vfp_arm.s
new file mode 100644
index 0000000000000000000000000000000000000000..3cc11b298b760222da80284c2de627bf612255c8
--- /dev/null
+++ b/src/cmd/dist/vfp_arm.s
@@ -0,0 +1,15 @@
+// Copyright 2015 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 "textflag.h"
+
+// try to run "vmov.f64 d0, d0" instruction
+TEXT useVFPv1(SB),NOSPLIT,$0
+	VMOV.F64 D0, D0
+	RET
+
+// try to run VFPv3-only "vmov.f64 d0, #112" instruction
+TEXT useVFPv3(SB),NOSPLIT,$0
+	VMOV.F64 $112, D0
+	RET
diff --git a/src/cmd/dist/vfp_default.s b/src/cmd/dist/vfp_default.s
new file mode 100644
index 0000000000000000000000000000000000000000..c795b357f7c9061d389e1c1d1280bb4dc9f7fcca
--- /dev/null
+++ b/src/cmd/dist/vfp_default.s
@@ -0,0 +1,14 @@
+// Copyright 2015 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.
+
+// +build !arm
+
+#include "textflag.h"
+
+TEXT ·useVFPv1(SB),NOSPLIT,$0
+	RET
+
+TEXT ·useVFPv3(SB),NOSPLIT,$0
+	RET
+
diff --git a/src/make.bash b/src/make.bash
index a90937a77e7ccfa0e2f049c01140cfe31377819d..54c4d61249ac90258fb3ee1971b611cd07d008d7 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -3,6 +3,8 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
+# See golang.org/s/go15bootstrap for an overview of the build process.
+
 # Environment variables that control make.bash:
 #
 # GOROOT_FINAL: The expected final Go root, baked into binaries.
@@ -110,26 +112,16 @@ rm -f ./runtime/runtime_defs.go
 
 # Finally!  Run the build.
 
-echo '##### Building C bootstrap tool.'
+echo '##### Building Go bootstrap tool.'
 echo cmd/dist
 export GOROOT="$(cd .. && pwd)"
-GOROOT_FINAL="${GOROOT_FINAL:-$GOROOT}"
-DEFGOROOT='-DGOROOT_FINAL="'"$GOROOT_FINAL"'"'
-
-mflag=""
-case "$GOHOSTARCH" in
-386) mflag=-m32;;
-amd64) mflag=-m64;;
-esac
-if [ "$(uname)" == "Darwin" ]; then
-	# golang.org/issue/5261
-	mflag="$mflag -mmacosx-version-min=10.6"
-fi
-# if gcc does not exist and $CC is not set, try clang if available.
-if [ -z "$CC" -a -z "$(type -t gcc)" -a -n "$(type -t clang)" ]; then
-	export CC=clang CXX=clang++
+GOROOT_BOOTSTRAP=${GOROOT_BOOTSTRAP:-$HOME/go1.4}
+if [ ! -x "$GOROOT_BOOTSTRAP/bin/go" ]; then
+	echo "ERROR: Cannot find $GOROOT_BOOTSTRAP/bin/go." >&2
+	echo "Set \$GOROOT_BOOTSTRAP to a working Go tree >= Go 1.4." >&2
 fi
-${CC:-gcc} $mflag -O2 -Wall -Werror -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c
+rm -f cmd/dist/dist
+GOROOT="$GOROOT_BOOTSTRAP" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist
 
 # -e doesn't propagate out of eval, so check success by hand.
 eval $(./cmd/dist/dist env -p || echo FAIL=true)
diff --git a/src/sudo.bash b/src/sudo.bash
deleted file mode 100755
index 33254c2c5e6d55e3d60c78192038531c18fa5261..0000000000000000000000000000000000000000
--- a/src/sudo.bash
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-# 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.
-
-set -e
-
-case "`uname`" in
-Darwin)
-	;;
-*)
-	exit 0
-esac
-
-# Check that the go command exists
-if ! go help >/dev/null 2>&1; then
-	echo "The go command is not in your PATH." >&2
-	exit 2
-fi
-
-eval $(go env)
-if ! [ -x $GOTOOLDIR/prof ]; then
-	echo "You don't need to run sudo.bash." >&2
-	exit 2
-fi
-
-if [[ ! -d /usr/local/bin ]]; then
-	echo 1>&2 'sudo.bash: problem with /usr/local/bin; cannot install tools.'
-	exit 2
-fi
-
-cd $(dirname $0)
-for i in prof
-do
-	# Remove old binaries if present
-	sudo rm -f /usr/local/bin/6$i
-	# Install new binaries
-	sudo cp $GOTOOLDIR/$i /usr/local/bin/go$i
-	sudo chgrp procmod /usr/local/bin/go$i
-	sudo chmod g+s /usr/local/bin/go$i
-done