Repository: go
Issue: cmd/go: cgo #line directives cause non-reproducibility
Labels: NeedsFix

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.13.5 darwin/amd64
</pre>

### Does this issue reproduce with the latest release?

yes

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/jayconrod/Library/Caches/go-build"
GOENV="/Users/jayconrod/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/jayconrod/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/opt/go/installed"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/opt/go/installed/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/jayconrod/Code/test/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/rq/x0692kqj6ml8cvrhcqh5bswc008xj1/T/go-build209284358=/tmp/go-build -gno-record-gcc-switches -fno-common"
</pre></details>

### What did you do?

Build an executable that depends on one or more cgo packages using `-trimpath`.

This can be reproduced by adding some cgo code to the program in  `build_trimpath.txt`, the test case that covers `-trimpath`.

```
diff --git a/src/cmd/go/testdata/script/build_trimpath.txt b/src/cmd/go/testdata/script/build_trimpath.txt
index cfab80743e..363a01f156 100644
--- a/src/cmd/go/testdata/script/build_trimpath.txt
+++ b/src/cmd/go/testdata/script/build_trimpath.txt
@@ -100,6 +100,9 @@ import (
 	"strings"
 )
 
+// const int x = 42;
+import "C"
+
 func main() {
 	exe := os.Args[1]
 	data, err := ioutil.ReadFile(exe)
```

Also observe that `strings $(go env GOROOT)/pkg/darwin_amd64/os/user.a` prints file names in random temporary directories under `/private/var`, indicating these paths weren't completely scrubbed from standard library packages that include cgo.

Below is a longer test case. It builds a small cgo program, then lists file names in the DWARF data in the executable. The working directory should not be listed (but it is).

```
go build -trimpath -o hello .
go run dwarf-list-files.go hello
! stdout $WORK

-- go.mod --
module example.com/go-reproducibility

go 1.13

-- hello.go --
package main

// const int x = 42;
import "C"
import "fmt"

func main() {
	fmt.Println(C.x)
}

-- dwarf-list-files.go --
// +build tool

package main

import (
	"debug/dwarf"
	"debug/macho"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
)

func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "usage: dwarf-list-files file\n")
		os.Exit(1)
	}
	files, err := run(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	for _, file := range files {
		fmt.Println(file)
	}
}

func run(path string) ([]string, error) {
	machoFile, err := macho.Open(path)
	if err != nil {
		return nil, err
	}
	defer machoFile.Close()

	dwarfData, err := machoFile.DWARF()
	if err != nil {
		return nil, err
	}

	dwarfReader := dwarfData.Reader()
	files := make(map[string]bool)
	for {
		e, err := dwarfReader.Next()
		if err != nil {
			return nil, err
		}
		if e == nil {
			break
		}
		lr, err := dwarfData.LineReader(e)
		if err != nil {
			return nil, err
		}
		if lr == nil {
			continue
		}

		var le dwarf.LineEntry
		for {
			if err := lr.Next(&le); err != nil {
				if err == io.EOF {
					break
				}
				return nil, err
			}
			files[le.File.Name] = true
		}
	}

	sortedFiles := make([]string, 0, len(files))
	for file := range files {
		sortedFiles = append(sortedFiles, file)
	}
	sort.Strings(sortedFiles)

	return sortedFiles, nil
}
```

### What did you expect to see?

When the `-trimpath` flag is used, the package root directories (including GOROOT, GOPATH roots, and module roots) should not appear in binaries produced by the `go` command or in any intermediate file.

### What did you see instead?

.c files created by cgo include `#line` directives that point back to the original source files. These .c files are intermediate files, so with `-trimpath`, they shouldn't include absolute paths. The paths should just be derived from the import path, the module path, and the version.

When we compile these files, we use `-fdebug-prefix-map` to strip the temporary working directory, but we don't use that flag to remove the source directory. If `#line` directives are correctly stripped, we don't need this for intermediate `.c` files, but we still need it for source `.c` files. Without this flag, absolute paths to the source directory end up in the DWARF data in the compiled `.o` files.

I don't know much about the DWARF format, but I think paths are compressed in the linked binaries we're producing. `strings` doesn't show any inappropriate source paths, but `dwarf-list-files.go` in the test case above does. Also, it seems like the linker has special handling for `GOROOT_FINAL` for binaries in `GOROOT`, but I'm not sure how that works.