cmd/release: sanity check relocations before release

Updates golang/go#31293

Change-Id: I001e1f1518c302a9e8c49716d44860603fd5c28b
Reviewed-on: https://go-review.googlesource.com/c/build/+/171317
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/cmd/release/release.go b/cmd/release/release.go
index 0cf1e20..a6fae05 100644
--- a/cmd/release/release.go
+++ b/cmd/release/release.go
@@ -401,6 +401,10 @@
 		return fmt.Errorf("Build failed: %v\nOutput:\n%v", remoteErr, out)
 	}
 
+	if err := b.checkRelocations(client); err != nil {
+		return err
+	}
+
 	goCmd := path.Join(goDir, "bin/go")
 	if b.OS == "windows" {
 		goCmd += ".exe"
@@ -701,6 +705,32 @@
 	return nil
 }
 
+// checkRelocations runs readelf on pkg/linux_amd64/runtime/cgo.a and makes sure
+// we don't see R_X86_64_REX_GOTPCRELX. See issue 31293.
+func (b *Build) checkRelocations(client *buildlet.Client) error {
+	if b.OS != "linux" || b.Arch != "amd64" {
+		return nil
+	}
+	var out bytes.Buffer
+	file := fmt.Sprintf("go/pkg/linux_%s/runtime/cgo.a", b.Arch)
+	remoteErr, err := client.Exec("readelf", buildlet.ExecOpts{
+		Output:      &out,
+		Args:        []string{"-r", "--wide", file},
+		SystemLevel: true, // look for readelf in system's PATH
+	})
+	if err != nil {
+		return fmt.Errorf("failed to run readelf: %v", err)
+	}
+	got := out.String()
+	if strings.Contains(got, "R_X86_64_REX_GOTPCRELX") {
+		return fmt.Errorf("%s contained a R_X86_64_REX_GOTPCRELX relocation", file)
+	}
+	if !strings.Contains(got, "R_X86_64_GOTPCREL") {
+		return fmt.Errorf("%s did not contain a R_X86_64_GOTPCREL relocation; remoteErr=%v, %s", file, remoteErr, got)
+	}
+	return nil
+}
+
 // verifyGzipSingleStream verifies that the named gzip file is not
 // a multi-stream file. See golang.org/issue/19052
 func verifyGzipSingleStream(name string) error {