cmd/link: strip STAB (symbolic debugging) symbols on darwin

On darwin, with external linking, the system linker produces STAB
(symbolic debugging) symbols in the binary's symbol table. These
include paths of the intermediate object files, like
<tmpdir>/go.o, which changes from run to run, making the build
non-reproducible.

Since we run dsymutil to produce debug info and combine them
back into the binary, we don't need those STAB symbols anymore.
Strip them after running dsymutil.

If DWARF is not enabled, we don't run dsymutil. We can pass
"-Wl,-S" to let the system linker not generate those symbols.

While here, also make it more consistent about DWARF combining.
Currently we only do DWARF combining on macOS/AMD64, when DWARF
is enabled. On ARM64, we run dsymutil, but then throw the result
away. This CL changes it to not run dsymutil (and strip) on
ARM64.

TODO: add a test. We don't do it here as it fails on some
(non-darwin) platforms.

Fixes #40979.

Change-Id: If770f7828cdb858857d6079e0585bf067f8f7a92
Reviewed-on: https://go-review.googlesource.com/c/go/+/250944
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 702c902..54ac109 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1238,6 +1238,10 @@
 		}
 	}
 
+	// On darwin, whether to combine DWARF into executable.
+	// Only macOS supports unmapped segments such as our __DWARF segment.
+	combineDwarf := ctxt.IsDarwin() && !*FlagS && !*FlagW && !debug_s && machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64()
+
 	switch ctxt.HeadType {
 	case objabi.Hdarwin:
 		if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
@@ -1248,6 +1252,9 @@
 		if ctxt.DynlinkingGo() && !ctxt.Arch.InFamily(sys.ARM, sys.ARM64) {
 			argv = append(argv, "-Wl,-flat_namespace")
 		}
+		if !combineDwarf {
+			argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols
+		}
 	case objabi.Hopenbsd:
 		argv = append(argv, "-Wl,-nopie")
 	case objabi.Hwindows:
@@ -1587,11 +1594,16 @@
 		ctxt.Logf("%s", out)
 	}
 
-	if !*FlagS && !*FlagW && !debug_s && ctxt.HeadType == objabi.Hdarwin {
+	if combineDwarf {
 		dsym := filepath.Join(*flagTmpdir, "go.dwarf")
 		if out, err := exec.Command("dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
 			Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
 		}
+		// Remove STAB (symbolic debugging) symbols after we are done with them (by dsymutil).
+		// They contain temporary file paths and make the build not reproducible.
+		if out, err := exec.Command("strip", "-S", *flagOutfile).CombinedOutput(); err != nil {
+			Exitf("%s: running strip failed: %v\n%s", os.Args[0], err, out)
+		}
 		// Skip combining if `dsymutil` didn't generate a file. See #11994.
 		if _, err := os.Stat(dsym); os.IsNotExist(err) {
 			return
@@ -1607,15 +1619,12 @@
 		if err != nil {
 			Exitf("%s: parsing Mach-O header failed: %v", os.Args[0], err)
 		}
-		// Only macOS supports unmapped segments such as our __DWARF segment.
-		if machoPlatform == PLATFORM_MACOS && ctxt.IsAMD64() {
-			if err := machoCombineDwarf(ctxt, exef, exem, dsym, combinedOutput); err != nil {
-				Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
-			}
-			os.Remove(*flagOutfile)
-			if err := os.Rename(combinedOutput, *flagOutfile); err != nil {
-				Exitf("%s: %v", os.Args[0], err)
-			}
+		if err := machoCombineDwarf(ctxt, exef, exem, dsym, combinedOutput); err != nil {
+			Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+		}
+		os.Remove(*flagOutfile)
+		if err := os.Rename(combinedOutput, *flagOutfile); err != nil {
+			Exitf("%s: %v", os.Args[0], err)
 		}
 	}
 }