Permalink
Browse files

cmd/go, cmd/link: make builds deterministic

Add the following flags when supported by the compiler:
  -gno-record-gcc-switches
  -fdebug-prefix-map=$WORK=/tmp/go-build

Add an empty NAME symbol to the ELF .symtab. GNU ld will add a NAME
symbol when one is not present; including one of our own prevents it
from adding a reference to the link tempdir.

Fixes #13247 for compilers that support -fdebug-prefix-map. (gcc, clang
in the near future.)

Change-Id: I221c71fc59cd23ee8c99bcc038793ff4623c9ffc
Reviewed-on: https://go-review.googlesource.com/19363
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Damien Neil <dneil@google.com>
  • Loading branch information...
neild committed Feb 9, 2016
1 parent 1a94431 commit 5bbb98df0960f57dca73cb7640456608d4cc0917
Showing with 60 additions and 12 deletions.
  1. +28 −12 src/cmd/go/build.go
  2. +27 −0 src/cmd/go/go_test.go
  3. +5 −0 src/cmd/link/internal/ld/symtab.go
@@ -686,6 +686,7 @@ type builder struct {
work string // the temporary work directory (ends in filepath.Separator)
actionCache map[cacheKey]*action // a cache of already-constructed actions
mkdirCache map[string]bool // a cache of created directories
flagCache map[string]bool // a cache of supported compiler flags
print func(args ...interface{}) (int, error)
output sync.Mutex
@@ -2927,6 +2928,14 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
// disable word wrapping in error messages
a = append(a, "-fmessage-length=0")
// Tell gcc not to include the work directory in object files.
if b.gccSupportsFlag("-fdebug-prefix-map=a=b") {
// -gno-record-gcc-switches is supported by all gcc/clang
// versions that support -fdebug-prefix-map.
a = append(a, "-gno-record-gcc-switches")
a = append(a, "-fdebug-prefix-map="+b.work+"=/tmp/go-build")
}
// On OS X, some of the compilers behave as if -fno-common
// is always set, and the Mach-O linker in 6l/8l assumes this.
// See https://golang.org/issue/3253.
@@ -2941,19 +2950,24 @@ func (b *builder) ccompilerCmd(envvar, defcmd, objdir string) []string {
// -no-pie must be passed when doing a partial link with -Wl,-r. But -no-pie is
// not supported by all compilers.
func (b *builder) gccSupportsNoPie() bool {
if goos != "linux" {
// On some BSD platforms, error messages from the
// compiler make it to the console despite cmd.Std*
// all being nil. As -no-pie is only required on linux
// systems so far, we only test there.
return false
return b.gccSupportsFlag("-no-pie")
}
// gccSupportsFlag checks to see if the compiler supports a flag.
func (b *builder) gccSupportsFlag(flag string) bool {
b.exec.Lock()
defer b.exec.Unlock()
if b, ok := b.flagCache[flag]; ok {
return b
}
src := filepath.Join(b.work, "trivial.c")
if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil {
return false
if b.flagCache == nil {
src := filepath.Join(b.work, "trivial.c")
if err := ioutil.WriteFile(src, []byte{}, 0666); err != nil {
return false
}
b.flagCache = make(map[string]bool)
}
cmdArgs := b.gccCmd(b.work)
cmdArgs = append(cmdArgs, "-no-pie", "-c", "trivial.c")
cmdArgs := append(envList("CC", defaultCC), flag, "-c", "trivial.c")
if buildN || buildX {
b.showcmd(b.work, "%s", joinUnambiguously(cmdArgs))
if buildN {
@@ -2964,7 +2978,9 @@ func (b *builder) gccSupportsNoPie() bool {
cmd.Dir = b.work
cmd.Env = envForDir(cmd.Dir, os.Environ())
out, err := cmd.CombinedOutput()
return err == nil && !bytes.Contains(out, []byte("unrecognized"))
supported := err == nil && !bytes.Contains(out, []byte("unrecognized"))
b.flagCache[flag] = supported
return supported
}
// gccArchArgs returns arguments to pass to gcc based on the architecture.
@@ -2759,3 +2759,30 @@ func TestParallelTest(t *testing.T) {
tg.setenv("GOPATH", tg.path("."))
tg.run("test", "-p=4", "p1", "p2", "p3", "p4")
}
func TestCgoConsistentResults(t *testing.T) {
if !canCgo {
t.Skip("skipping because cgo not enabled")
}
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.makeTempdir()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
exe1 := tg.path("cgotest1" + exeSuffix)
exe2 := tg.path("cgotest2" + exeSuffix)
tg.run("build", "-o", exe1, "cgotest")
tg.run("build", "-x", "-o", exe2, "cgotest")
b1, err := ioutil.ReadFile(exe1)
tg.must(err)
b2, err := ioutil.ReadFile(exe2)
tg.must(err)
if !tg.doGrepMatch(`-fdebug-prefix-map=\$WORK`, &tg.stderr) {
t.Skip("skipping because C compiler does not support -fdebug-prefix-map")
}
if !bytes.Equal(b1, b2) {
t.Error("building cgotest twice did not produce the same output")
}
}
@@ -215,6 +215,11 @@ func Asmelfsym() {
dwarfaddelfsectionsyms()
// Some linkers will add a FILE sym if one is not present.
// Avoid having the working directory inserted into the symbol table.
putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_FILE, SHN_ABS, 0)
numelfsym++
elfbind = STB_LOCAL
genasmsym(putelfsym)

0 comments on commit 5bbb98d

Please sign in to comment.