diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index ee99bfc2c35528e3d7a203050189fbbc88165f35..796d11a63c286c6168881a8ade7f9d5441bd1869 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -102,11 +102,13 @@ the use of cgo, and to 0 to disable it. The go tool will set the
 build constraint "cgo" if cgo is enabled.
 
 When cross-compiling, you must specify a C cross-compiler for cgo to
-use. You can do this by setting the CC_FOR_TARGET environment
-variable when building the toolchain using make.bash, or by setting
-the CC environment variable any time you run the go tool. The
-CXX_FOR_TARGET and CXX environment variables work in a similar way for
-C++ code.
+use. You can do this by setting the generic CC_FOR_TARGET or the
+more specific CC_FOR_${GOOS}_${GOARCH} (for example, CC_FOR_linux_arm)
+environment variable when building the toolchain using make.bash,
+or you can set the CC environment variable any time you run the go tool.
+
+The CXX_FOR_TARGET, CXX_FOR_${GOOS}_${GOARCH}, and CXX
+environment variables work in a similar way for C++ code.
 
 Go references to C
 
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 99e98cee75575df524fbc57f8406e023e81b9a1a..95be03f6e45f4f3579c8ba1757aab3da8014d781 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -1221,7 +1221,7 @@ func (p *Package) gccBaseCmd() []string {
 	if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 {
 		return ret
 	}
-	return strings.Fields(defaultCC)
+	return strings.Fields(defaultCC(goos, goarch))
 }
 
 // gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index aa2402163d6963e94ae0370c5a51c280b13448df..a2f3a8c2822209933a45dd1a9bac8bc8e0fef142 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -23,29 +23,28 @@ import (
 
 // The usual variables.
 var (
-	goarch                 string
-	gobin                  string
-	gohostarch             string
-	gohostos               string
-	goos                   string
-	goarm                  string
-	go386                  string
-	goroot                 string
-	goroot_final           string
-	goextlinkenabled       string
-	gogcflags              string // For running built compiler
-	goldflags              string
-	workdir                string
-	tooldir                string
-	oldgoos                string
-	oldgoarch              string
-	exe                    string
-	defaultcc              string
-	defaultcflags          string
-	defaultldflags         string
-	defaultcxxtarget       string
-	defaultcctarget        string
-	defaultpkgconfigtarget string
+	goarch           string
+	gobin            string
+	gohostarch       string
+	gohostos         string
+	goos             string
+	goarm            string
+	go386            string
+	goroot           string
+	goroot_final     string
+	goextlinkenabled string
+	gogcflags        string // For running built compiler
+	goldflags        string
+	workdir          string
+	tooldir          string
+	oldgoos          string
+	oldgoarch        string
+	exe              string
+	defaultcc        map[string]string
+	defaultcxx       map[string]string
+	defaultcflags    string
+	defaultldflags   string
+	defaultpkgconfig string
 
 	rebuildall   bool
 	defaultclang bool
@@ -172,49 +171,21 @@ func xinit() {
 
 	gogcflags = os.Getenv("BOOT_GO_GCFLAGS")
 
-	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 {
-			b = "clang"
-		} else {
-			b = "gcc"
-		}
+	cc := "gcc"
+	if defaultclang {
+		cc = "clang"
 	}
-	defaultcc = b
+	defaultcc = compilerEnv("CC", cc)
+	defaultcxx = compilerEnv("CXX", cc+"++")
 
 	defaultcflags = os.Getenv("CFLAGS")
-
 	defaultldflags = os.Getenv("LDFLAGS")
 
-	b = os.Getenv("CC_FOR_TARGET")
-	if b == "" {
-		b = defaultcc
-	}
-	defaultcctarget = b
-
-	b = os.Getenv("CXX_FOR_TARGET")
-	if b == "" {
-		b = os.Getenv("CXX")
-		if b == "" {
-			if defaultclang {
-				b = "clang++"
-			} else {
-				b = "g++"
-			}
-		}
-	}
-	defaultcxxtarget = b
-
 	b = os.Getenv("PKG_CONFIG")
 	if b == "" {
 		b = "pkg-config"
 	}
-	defaultpkgconfigtarget = b
+	defaultpkgconfig = b
 
 	// For tools being invoked but also for os.ExpandEnv.
 	os.Setenv("GO386", go386)
@@ -244,6 +215,55 @@ func xinit() {
 	tooldir = pathf("%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch)
 }
 
+// compilerEnv returns a map from "goos/goarch" to the
+// compiler setting to use for that platform.
+// The entry for key "" covers any goos/goarch not explicitly set in the map.
+// For example, compilerEnv("CC", "gcc") returns the C compiler settings
+// read from $CC, defaulting to gcc.
+//
+// The result is a map because additional environment variables
+// can be set to change the compiler based on goos/goarch settings.
+// The following applies to all envNames but CC is assumed to simplify
+// the presentation.
+//
+// If no environment variables are set, we use def for all goos/goarch.
+// $CC, if set, applies to all goos/goarch but is overridden by the following.
+// $CC_FOR_TARGET, if set, applies to all goos/goarch except gohostos/gohostarch,
+// but is overridden by the following.
+// If gohostos=goos and gohostarch=goarch, then $CC_FOR_TARGET applies even for gohostos/gohostarch.
+// $CC_FOR_goos_goarch, if set, applies only to goos/goarch.
+func compilerEnv(envName, def string) map[string]string {
+	m := map[string]string{"": def}
+
+	if env := os.Getenv(envName); env != "" {
+		m[""] = env
+	}
+	if env := os.Getenv(envName + "_FOR_TARGET"); env != "" {
+		if gohostos != goos || gohostarch != goarch {
+			m[gohostos+"/"+gohostarch] = m[""]
+		}
+		m[""] = env
+	}
+
+	for _, goos := range okgoos {
+		for _, goarch := range okgoarch {
+			if env := os.Getenv(envName + "_FOR_" + goos + "_" + goarch); env != "" {
+				m[goos+"/"+goarch] = env
+			}
+		}
+	}
+
+	return m
+}
+
+// compilerEnvLookup returns the compiler settings for goos/goarch in map m.
+func compilerEnvLookup(m map[string]string, goos, goarch string) string {
+	if cc := m[goos+"/"+goarch]; cc != "" {
+		return cc
+	}
+	return m[""]
+}
+
 // rmworkdir deletes the work directory.
 func rmworkdir() {
 	if vflag > 1 {
@@ -1009,8 +1029,6 @@ func cmdenv() {
 		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)
@@ -1171,12 +1189,7 @@ func cmdbootstrap() {
 		xprintf("\n")
 	}
 	xprintf("Building Go toolchain2 using go_bootstrap and Go toolchain1.\n")
-	os.Setenv("CC", defaultcc)
-	if goos == oldgoos && goarch == oldgoarch {
-		// Host and target are same, and we have historically
-		// chosen $CC_FOR_TARGET in this case.
-		os.Setenv("CC", defaultcctarget)
-	}
+	os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch))
 	goInstall(goBootstrap, append([]string{"-i"}, toolchain...)...)
 	if debug {
 		run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
@@ -1241,7 +1254,7 @@ func cmdbootstrap() {
 		goarch = oldgoarch
 		os.Setenv("GOOS", goos)
 		os.Setenv("GOARCH", goarch)
-		os.Setenv("CC", defaultcctarget)
+		os.Setenv("CC", compilerEnvLookup(defaultcc, goos, goarch))
 		xprintf("Building packages and commands for target, %s/%s.\n", goos, goarch)
 	}
 	goInstall(goBootstrap, "std", "cmd")
@@ -1372,7 +1385,7 @@ func checkCC() {
 	if !needCC() {
 		return
 	}
-	if output, err := exec.Command(defaultcc, "--help").CombinedOutput(); err != nil {
+	if output, err := exec.Command(defaultcc[""], "--help").CombinedOutput(); err != nil {
 		outputHdr := ""
 		if len(output) > 0 {
 			outputHdr = "\nCommand output:\n\n"
diff --git a/src/cmd/dist/buildgo.go b/src/cmd/dist/buildgo.go
index 19384a1a530c242af9f2eef956ce16a17e1815f4..caafc13da888ad2ceec2b30bc73823fe1051177a 100644
--- a/src/cmd/dist/buildgo.go
+++ b/src/cmd/dist/buildgo.go
@@ -33,9 +33,9 @@ func mkzdefaultcc(dir, file string) {
 		fmt.Fprintln(&buf)
 		fmt.Fprintf(&buf, "package cfg\n")
 		fmt.Fprintln(&buf)
-		fmt.Fprintf(&buf, "const DefaultCC = `%s`\n", defaultcctarget)
-		fmt.Fprintf(&buf, "const DefaultCXX = `%s`\n", defaultcxxtarget)
-		fmt.Fprintf(&buf, "const DefaultPkgConfig = `%s`\n", defaultpkgconfigtarget)
+		fmt.Fprintf(&buf, "const DefaultPkgConfig = `%s`\n", defaultpkgconfig)
+		buf.WriteString(defaultCCFunc("DefaultCC", defaultcc))
+		buf.WriteString(defaultCCFunc("DefaultCXX", defaultcxx))
 		writefile(buf.String(), file, writeSkipSame)
 		return
 	}
@@ -45,12 +45,34 @@ func mkzdefaultcc(dir, file string) {
 	fmt.Fprintln(&buf)
 	fmt.Fprintf(&buf, "package main\n")
 	fmt.Fprintln(&buf)
-	fmt.Fprintf(&buf, "const defaultCC = `%s`\n", defaultcctarget)
-	fmt.Fprintf(&buf, "const defaultCXX = `%s`\n", defaultcxxtarget)
-	fmt.Fprintf(&buf, "const defaultPkgConfig = `%s`\n", defaultpkgconfigtarget)
+	fmt.Fprintf(&buf, "const defaultPkgConfig = `%s`\n", defaultpkgconfig)
+	buf.WriteString(defaultCCFunc("defaultCC", defaultcc))
+	buf.WriteString(defaultCCFunc("defaultCXX", defaultcxx))
 	writefile(buf.String(), file, writeSkipSame)
 }
 
+func defaultCCFunc(name string, defaultcc map[string]string) string {
+	var buf bytes.Buffer
+
+	fmt.Fprintf(&buf, "func %s(goos, goarch string) string {\n", name)
+	fmt.Fprintf(&buf, "\tswitch goos+`/`+goarch {\n")
+	var keys []string
+	for k := range defaultcc {
+		if k != "" {
+			keys = append(keys, k)
+		}
+	}
+	sort.Strings(keys)
+	for _, k := range keys {
+		fmt.Fprintf(&buf, "\tcase %q:\n\t\treturn %q\n", k, defaultcc[k])
+	}
+	fmt.Fprintf(&buf, "\t}\n")
+	fmt.Fprintf(&buf, "\treturn %q\n", defaultcc[""])
+	fmt.Fprintf(&buf, "}\n")
+
+	return buf.String()
+}
+
 // mkzcgo writes zosarch.go for cmd/go.
 func mkzosarch(dir, file string) {
 	// sort for deterministic zosarch.go file
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index 90ab2d718fe0c31d57d7e38bbc019af078afc68d..f756e3b60722749737041d49bbcce8f93ea127da 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -78,11 +78,11 @@ func MkEnv() []cfg.EnvVar {
 		env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386})
 	}
 
-	cc := cfg.DefaultCC
+	cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
 	if env := strings.Fields(os.Getenv("CC")); len(env) > 0 {
 		cc = env[0]
 	}
-	cxx := cfg.DefaultCXX
+	cxx := cfg.DefaultCXX(cfg.Goos, cfg.Goarch)
 	if env := strings.Fields(os.Getenv("CXX")); len(env) > 0 {
 		cxx = env[0]
 	}
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 7a4e62b0a4466fa4dca187ea3395eddd9f679bfb..d43a5f2417f0d362e026d4b5fb206dec94a92ca3 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -1597,12 +1597,12 @@ func (b *Builder) gfortranCmd(incdir, workdir string) []string {
 
 // ccExe returns the CC compiler setting without all the extra flags we add implicitly.
 func (b *Builder) ccExe() []string {
-	return b.compilerExe(origCC, cfg.DefaultCC)
+	return b.compilerExe(origCC, cfg.DefaultCC(cfg.Goos, cfg.Goarch))
 }
 
 // cxxExe returns the CXX compiler setting without all the extra flags we add implicitly.
 func (b *Builder) cxxExe() []string {
-	return b.compilerExe(origCXX, cfg.DefaultCXX)
+	return b.compilerExe(origCXX, cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
 }
 
 // fcExe returns the FC compiler setting without all the extra flags we add implicitly.
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index 651d20c21c57197333e81796267522892684b598..c0db90dfe50561dd19fe9f090736210c8bd0bca9 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -430,9 +430,9 @@ func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string)
 	// Else, use the CC environment variable and defaultCC as fallback.
 	var compiler []string
 	if cxx {
-		compiler = envList("CXX", cfg.DefaultCXX)
+		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
 	} else {
-		compiler = envList("CC", cfg.DefaultCC)
+		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
 	}
 	ldflags = append(ldflags, "-buildmode="+ldBuildmode)
 	if root.buildID != "" {
@@ -474,9 +474,9 @@ func (gcToolchain) ldShared(b *Builder, toplevelactions []*Action, out, importcf
 	// Else, use the CC environment variable and defaultCC as fallback.
 	var compiler []string
 	if cxx {
-		compiler = envList("CXX", cfg.DefaultCXX)
+		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
 	} else {
-		compiler = envList("CC", cfg.DefaultCC)
+		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
 	}
 	ldflags = setextld(ldflags, compiler)
 	for _, d := range toplevelactions {
diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go
index 1d7155c977601982125f2b652636402ee26d5658..898c3c200486a76a821229e823c503e2695486f8 100644
--- a/src/cmd/go/internal/work/gccgo.go
+++ b/src/cmd/go/internal/work/gccgo.go
@@ -464,7 +464,7 @@ func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error
 		defs = append(defs, "-fsplit-stack")
 	}
 	defs = tools.maybePIC(defs)
-	return b.run(p.Dir, p.ImportPath, nil, envList("CC", cfg.DefaultCC), "-Wall", "-g",
+	return b.run(p.Dir, p.ImportPath, nil, envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)), "-Wall", "-g",
 		"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
 }