diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index dace766aed894e66f2cd3bb2e0f146864ca6f514..ae73c92e946be649f6d26a1a26844e5cbefc3fa1 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -829,15 +829,6 @@ var GoTools = map[string]targetDir{
 	"code.google.com/p/go.tools/cmd/vet":   StalePath,
 }
 
-var raceExclude = map[string]bool{
-	"runtime/race": true,
-	"runtime/msan": true,
-	"runtime/cgo":  true,
-	"cmd/cgo":      true,
-	"syscall":      true,
-	"errors":       true,
-}
-
 var cgoExclude = map[string]bool{
 	"runtime/cgo": true,
 }
@@ -934,47 +925,52 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
 		}
 	}
 
+	// Build augmented import list to add implicit dependencies.
+	// Be careful not to add imports twice, just to avoid confusion.
 	importPaths := p.Imports
-	// Packages that use cgo import runtime/cgo implicitly.
-	// Packages that use cgo also import syscall implicitly,
-	// to wrap errno.
-	// Exclude certain packages to avoid circular dependencies.
+	addImport := func(path string) {
+		for _, p := range importPaths {
+			if path == p {
+				return
+			}
+		}
+		importPaths = append(importPaths, path)
+	}
+
+	// Cgo translation adds imports of "runtime/cgo" and "syscall",
+	// except for certain packages, to avoid circular dependencies.
 	if len(p.CgoFiles) > 0 && (!p.Standard || !cgoExclude[p.ImportPath]) {
-		importPaths = append(importPaths, "runtime/cgo")
+		addImport("runtime/cgo")
 	}
 	if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
-		importPaths = append(importPaths, "syscall")
-	}
-
-	if p.Name == "main" && cfg.ExternalLinkingForced(p.Goroot) {
-		importPaths = append(importPaths, "runtime/cgo")
+		addImport("syscall")
 	}
 
-	// Everything depends on runtime, except runtime, its internal
-	// subpackages, and unsafe.
-	if !p.Standard || (p.ImportPath != "runtime" && !strings.HasPrefix(p.ImportPath, "runtime/internal/") && p.ImportPath != "unsafe") {
-		importPaths = append(importPaths, "runtime")
-		// When race detection enabled everything depends on runtime/race.
-		// Exclude certain packages to avoid circular dependencies.
-		if cfg.BuildRace && (!p.Standard || !raceExclude[p.ImportPath]) {
-			importPaths = append(importPaths, "runtime/race")
-		}
-		// MSan uses runtime/msan.
-		if cfg.BuildMSan && (!p.Standard || !raceExclude[p.ImportPath]) {
-			importPaths = append(importPaths, "runtime/msan")
-		}
-		// On ARM with GOARM=5, everything depends on math for the link.
-		if p.Name == "main" && cfg.Goarch == "arm" {
-			importPaths = append(importPaths, "math")
+	// The linker loads implicit dependencies.
+	if p.Name == "main" && !p.Internal.ForceLibrary {
+		for _, dep := range LinkerDeps(p) {
+			addImport(dep)
 		}
 	}
 
-	// Runtime and its internal packages depend on runtime/internal/sys,
-	// so that they pick up the generated zversion.go file.
-	// This can be an issue particularly for runtime/internal/atomic;
-	// see issue 13655.
-	if p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal/")) && p.ImportPath != "runtime/internal/sys" {
-		importPaths = append(importPaths, "runtime/internal/sys")
+	// If runtime/internal/sys/zversion.go changes, it very likely means the
+	// compiler has been recompiled with that new version, so all existing
+	// archives are now stale. Make everything appear to import runtime/internal/sys,
+	// so that in this situation everything will appear stale and get recompiled.
+	// Due to the rules for visibility of internal packages, things outside runtime
+	// must import runtime, and runtime imports runtime/internal/sys.
+	// Content-based staleness that includes a check of the compiler version
+	// will make this hack unnecessary; once that lands, this whole comment
+	// and switch statement should be removed.
+	switch {
+	case p.Standard && p.ImportPath == "runtime/internal/sys":
+		// nothing
+	case p.Standard && p.ImportPath == "unsafe":
+		// nothing - not a real package, and used by runtime
+	case p.Standard && strings.HasPrefix(p.ImportPath, "runtime"):
+		addImport("runtime/internal/sys")
+	default:
+		addImport("runtime")
 	}
 
 	// Check for case-insensitive collision of input files.
@@ -1123,6 +1119,30 @@ func (p *Package) load(stk *ImportStack, bp *build.Package, err error) {
 	}
 }
 
+// LinkerDeps returns the list of linker-induced dependencies for p.
+func LinkerDeps(p *Package) []string {
+	var deps []string
+
+	// External linking mode forces an import of runtime/cgo.
+	if cfg.ExternalLinkingForced(p.Goroot) {
+		deps = append(deps, "runtime/cgo")
+	}
+	// On ARM with GOARM=5, it forces an import of math, for soft floating point.
+	if cfg.Goarch == "arm" {
+		deps = append(deps, "math")
+	}
+	// Using the race detector forces an import of runtime/race.
+	if cfg.BuildRace {
+		deps = append(deps, "runtime/race")
+	}
+	// Using memory sanitizer forces an import of runtime/msan.
+	if cfg.BuildMSan {
+		deps = append(deps, "runtime/msan")
+	}
+
+	return deps
+}
+
 // mkAbs rewrites list, which must be paths relative to p.Dir,
 // into a sorted list of absolute paths. It edits list in place but for
 // convenience also returns list back to its caller.
diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go
index c3810feb0d1b19586511ae66524e51eee5a1774e..96dced830ccaf73cd6e095543b7a0f4853c61335 100644
--- a/src/cmd/go/internal/test/test.go
+++ b/src/cmd/go/internal/test/test.go
@@ -816,10 +816,11 @@ func builderTest(b *work.Builder, p *load.Package) (buildAction, runAction, prin
 	}
 
 	// The generated main also imports testing, regexp, and os.
+	// Also the linker introduces implicit dependencies reported by LinkerDeps.
 	stk.Push("testmain")
-	deps := testMainDeps
-	if cfg.ExternalLinkingForced(pmain.Goroot) {
-		deps = str.StringList(deps, "runtime/cgo")
+	deps := testMainDeps // cap==len, so safe for append
+	for _, d := range load.LinkerDeps(p) {
+		deps = append(deps, d)
 	}
 	for _, dep := range deps {
 		if dep == ptest.ImportPath {
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 2cc1682b150fbb90fc239f22bf715cacd06a02ad..c62735d0aa4c91a1258d12f8245ee62971624952 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -1406,10 +1406,6 @@ func (b *Builder) build(a *Action) (err error) {
 			// This happens for gccgo-internal packages like runtime.
 			continue
 		}
-		// TODO(rsc): runtime/internal/sys appears twice sometimes,
-		// because of the blind append in ../load/pkg.go that
-		// claims to fix issue 13655. That's probably not the right fix.
-		// Look into that.
 		fmt.Fprintf(&icfg, "packagefile %s=%s\n", p1.ImportPath, p1.Internal.Pkgfile)
 	}