github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/work/gc.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package work
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"github.com/bir3/gocompiler/src/internal/platform"
    12  	"io"
    13  	"log"
    14  	"os"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strings"
    18  
    19  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/base"
    20  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg"
    21  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys"
    22  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/load"
    23  	"github.com/bir3/gocompiler/src/cmd/gocmd/internal/str"
    24  	"github.com/bir3/gocompiler/src/cmd/internal/objabi"
    25  	"github.com/bir3/gocompiler/src/cmd/internal/quoted"
    26  	"crypto/sha1"
    27  )
    28  
    29  // The 'path' used for GOROOT_FINAL when -trimpath is specified
    30  const trimPathGoRootFinal string = "$GOROOT"
    31  
    32  var runtimePackages = map[string]struct{}{
    33  	"internal/abi":             struct{}{},
    34  	"internal/bytealg":         struct{}{},
    35  	"internal/coverage/rtcov":  struct{}{},
    36  	"internal/cpu":             struct{}{},
    37  	"internal/goarch":          struct{}{},
    38  	"internal/goos":            struct{}{},
    39  	"runtime":                  struct{}{},
    40  	"runtime/internal/atomic":  struct{}{},
    41  	"runtime/internal/math":    struct{}{},
    42  	"runtime/internal/sys":     struct{}{},
    43  	"runtime/internal/syscall": struct{}{},
    44  }
    45  
    46  // The Go toolchain.
    47  
    48  type gcToolchain struct{}
    49  
    50  func (gcToolchain) compiler() string {
    51  	return base.Tool("compile")
    52  }
    53  
    54  func (gcToolchain) linker() string {
    55  	return base.Tool("link")
    56  }
    57  
    58  func pkgPath(a *Action) string {
    59  	p := a.Package
    60  	ppath := p.ImportPath
    61  	if cfg.BuildBuildmode == "plugin" {
    62  		ppath = pluginPath(a)
    63  	} else if p.Name == "main" && !p.Internal.ForceLibrary {
    64  		ppath = "main"
    65  	}
    66  	return ppath
    67  }
    68  
    69  func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
    70  	p := a.Package
    71  	objdir := a.Objdir
    72  	if archive != "" {
    73  		ofile = archive
    74  	} else {
    75  		out := "_go_.o"
    76  		ofile = objdir + out
    77  	}
    78  
    79  	pkgpath := pkgPath(a)
    80  	defaultGcFlags := []string{"-p", pkgpath}
    81  	if p.Module != nil {
    82  		v := p.Module.GoVersion
    83  		if v == "" {
    84  			// We started adding a 'go' directive to the go.mod file unconditionally
    85  			// as of Go 1.12, so any module that still lacks such a directive must
    86  			// either have been authored before then, or have a hand-edited go.mod
    87  			// file that hasn't been updated by cmd/go since that edit.
    88  			//
    89  			// Unfortunately, through at least Go 1.16 we didn't add versions to
    90  			// vendor/modules.txt. So this could also be a vendored 1.16 dependency.
    91  			//
    92  			// Fortunately, there were no breaking changes to the language between Go
    93  			// 1.11 and 1.16, so if we assume Go 1.16 semantics we will not introduce
    94  			// any spurious errors — we will only mask errors, and not particularly
    95  			// important ones at that.
    96  			v = "1.16"
    97  		}
    98  		if allowedVersion(v) {
    99  			defaultGcFlags = append(defaultGcFlags, "-lang=go"+v)
   100  		}
   101  	}
   102  	if p.Standard {
   103  		defaultGcFlags = append(defaultGcFlags, "-std")
   104  	}
   105  	_, compilingRuntime := runtimePackages[p.ImportPath]
   106  	compilingRuntime = compilingRuntime && p.Standard
   107  	if compilingRuntime {
   108  		// runtime compiles with a special gc flag to check for
   109  		// memory allocations that are invalid in the runtime package,
   110  		// and to implement some special compiler pragmas.
   111  		defaultGcFlags = append(defaultGcFlags, "-+")
   112  	}
   113  
   114  	// If we're giving the compiler the entire package (no C etc files), tell it that,
   115  	// so that it can give good error messages about forward declarations.
   116  	// Exceptions: a few standard packages have forward declarations for
   117  	// pieces supplied behind-the-scenes by package runtime.
   118  	extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
   119  	if p.Standard {
   120  		switch p.ImportPath {
   121  		case "bytes", "internal/poll", "net", "os":
   122  			fallthrough
   123  		case "runtime/metrics", "runtime/pprof", "runtime/trace":
   124  			fallthrough
   125  		case "sync", "syscall", "time":
   126  			extFiles++
   127  		}
   128  	}
   129  	if extFiles == 0 {
   130  		defaultGcFlags = append(defaultGcFlags, "-complete")
   131  	}
   132  	if cfg.BuildContext.InstallSuffix != "" {
   133  		defaultGcFlags = append(defaultGcFlags, "-installsuffix", cfg.BuildContext.InstallSuffix)
   134  	}
   135  	if a.buildID != "" {
   136  		defaultGcFlags = append(defaultGcFlags, "-buildid", a.buildID)
   137  	}
   138  	if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" {
   139  		defaultGcFlags = append(defaultGcFlags, "-dwarf=false")
   140  	}
   141  	if strings.HasPrefix(RuntimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") {
   142  		defaultGcFlags = append(defaultGcFlags, "-goversion", RuntimeVersion)
   143  	}
   144  	if p.Internal.CoverageCfg != "" {
   145  		defaultGcFlags = append(defaultGcFlags, "-coveragecfg="+p.Internal.CoverageCfg)
   146  	}
   147  	if cfg.BuildPGOFile != "" {
   148  		defaultGcFlags = append(defaultGcFlags, "-pgoprofile="+cfg.BuildPGOFile)
   149  	}
   150  	if symabis != "" {
   151  		defaultGcFlags = append(defaultGcFlags, "-symabis", symabis)
   152  	}
   153  
   154  	gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags)
   155  	if p.Internal.FuzzInstrument {
   156  		gcflags = append(gcflags, fuzzInstrumentFlags()...)
   157  	}
   158  	if compilingRuntime {
   159  		// Remove -N, if present.
   160  		// It is not possible to build the runtime with no optimizations,
   161  		// because the compiler cannot eliminate enough write barriers.
   162  		for i := 0; i < len(gcflags); i++ {
   163  			if gcflags[i] == "-N" {
   164  				copy(gcflags[i:], gcflags[i+1:])
   165  				gcflags = gcflags[:len(gcflags)-1]
   166  				i--
   167  			}
   168  		}
   169  	}
   170  	// Add -c=N to use concurrent backend compilation, if possible.
   171  	if c := gcBackendConcurrency(gcflags); c > 1 {
   172  		gcflags = append(gcflags, fmt.Sprintf("-c=%d", c))
   173  	}
   174  
   175  	args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags}
   176  	if p.Internal.LocalPrefix == "" {
   177  		args = append(args, "-nolocalimports")
   178  	} else {
   179  		args = append(args, "-D", p.Internal.LocalPrefix)
   180  	}
   181  	if importcfg != nil {
   182  		if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
   183  			return "", nil, err
   184  		}
   185  		args = append(args, "-importcfg", objdir+"importcfg")
   186  	}
   187  	if embedcfg != nil {
   188  		if err := b.writeFile(objdir+"embedcfg", embedcfg); err != nil {
   189  			return "", nil, err
   190  		}
   191  		args = append(args, "-embedcfg", objdir+"embedcfg")
   192  	}
   193  	if ofile == archive {
   194  		args = append(args, "-pack")
   195  	}
   196  	if asmhdr {
   197  		args = append(args, "-asmhdr", objdir+"go_asm.h")
   198  	}
   199  
   200  	for _, f := range gofiles {
   201  		f := mkAbs(p.Dir, f)
   202  
   203  		// Handle overlays. Convert path names using OverlayPath
   204  		// so these paths can be handed directly to tools.
   205  		// Deleted files won't show up in when scanning directories earlier,
   206  		// so OverlayPath will never return "" (meaning a deleted file) here.
   207  		// TODO(#39958): Handle cases where the package directory
   208  		// doesn't exist on disk (this can happen when all the package's
   209  		// files are in an overlay): the code expects the package directory
   210  		// to exist and runs some tools in that directory.
   211  		// TODO(#39958): Process the overlays when the
   212  		// gofiles, cgofiles, cfiles, sfiles, and cxxfiles variables are
   213  		// created in (*Builder).build. Doing that requires rewriting the
   214  		// code that uses those values to expect absolute paths.
   215  		f, _ = fsys.OverlayPath(f)
   216  
   217  		args = append(args, f)
   218  	}
   219  
   220  	output, err = b.runOut(a, base.Cwd(), nil, args...)
   221  	return ofile, output, err
   222  }
   223  
   224  // gcBackendConcurrency returns the backend compiler concurrency level for a package compilation.
   225  func gcBackendConcurrency(gcflags []string) int {
   226  	// First, check whether we can use -c at all for this compilation.
   227  	canDashC := concurrentGCBackendCompilationEnabledByDefault
   228  
   229  	switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
   230  	case "0":
   231  		canDashC = false
   232  	case "1":
   233  		canDashC = true
   234  	case "":
   235  		// Not set. Use default.
   236  	default:
   237  		log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
   238  	}
   239  
   240  	// TODO: Test and delete these conditions.
   241  	if cfg.ExperimentErr != nil || cfg.Experiment.FieldTrack || cfg.Experiment.PreemptibleLoops {
   242  		canDashC = false
   243  	}
   244  
   245  	if !canDashC {
   246  		return 1
   247  	}
   248  
   249  	// Decide how many concurrent backend compilations to allow.
   250  	//
   251  	// If we allow too many, in theory we might end up with p concurrent processes,
   252  	// each with c concurrent backend compiles, all fighting over the same resources.
   253  	// However, in practice, that seems not to happen too much.
   254  	// Most build graphs are surprisingly serial, so p==1 for much of the build.
   255  	// Furthermore, concurrent backend compilation is only enabled for a part
   256  	// of the overall compiler execution, so c==1 for much of the build.
   257  	// So don't worry too much about that interaction for now.
   258  	//
   259  	// However, in practice, setting c above 4 tends not to help very much.
   260  	// See the analysis in CL 41192.
   261  	//
   262  	// TODO(josharian): attempt to detect whether this particular compilation
   263  	// is likely to be a bottleneck, e.g. when:
   264  	//   - it has no successor packages to compile (usually package main)
   265  	//   - all paths through the build graph pass through it
   266  	//   - critical path scheduling says it is high priority
   267  	// and in such a case, set c to runtime.GOMAXPROCS(0).
   268  	// By default this is the same as runtime.NumCPU.
   269  	// We do this now when p==1.
   270  	// To limit parallelism, set GOMAXPROCS below numCPU; this may be useful
   271  	// on a low-memory builder, or if a deterministic build order is required.
   272  	c := runtime.GOMAXPROCS(0)
   273  	if cfg.BuildP == 1 {
   274  		// No process parallelism, do not cap compiler parallelism.
   275  		return c
   276  	}
   277  	// Some process parallelism. Set c to min(4, maxprocs).
   278  	if c > 4 {
   279  		c = 4
   280  	}
   281  	return c
   282  }
   283  
   284  // trimpath returns the -trimpath argument to use
   285  // when compiling the action.
   286  func (a *Action) trimpath() string {
   287  	// Keep in sync with Builder.ccompile
   288  	// The trimmed paths are a little different, but we need to trim in the
   289  	// same situations.
   290  
   291  	// Strip the object directory entirely.
   292  	objdir := a.Objdir
   293  	if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
   294  		objdir = objdir[:len(objdir)-1]
   295  	}
   296  	rewrite := ""
   297  
   298  	rewriteDir := a.Package.Dir
   299  	if cfg.BuildTrimpath {
   300  		importPath := a.Package.Internal.OrigImportPath
   301  		if m := a.Package.Module; m != nil && m.Version != "" {
   302  			rewriteDir = m.Path + "@" + m.Version + strings.TrimPrefix(importPath, m.Path)
   303  		} else {
   304  			rewriteDir = importPath
   305  		}
   306  		rewrite += a.Package.Dir + "=>" + rewriteDir + ";"
   307  	}
   308  
   309  	// Add rewrites for overlays. The 'from' and 'to' paths in overlays don't need to have
   310  	// same basename, so go from the overlay contents file path (passed to the compiler)
   311  	// to the path the disk path would be rewritten to.
   312  
   313  	cgoFiles := make(map[string]bool)
   314  	for _, f := range a.Package.CgoFiles {
   315  		cgoFiles[f] = true
   316  	}
   317  
   318  	// TODO(matloob): Higher up in the stack, when the logic for deciding when to make copies
   319  	// of c/c++/m/f/hfiles is consolidated, use the same logic that Build uses to determine
   320  	// whether to create the copies in objdir to decide whether to rewrite objdir to the
   321  	// package directory here.
   322  	var overlayNonGoRewrites string // rewrites for non-go files
   323  	hasCgoOverlay := false
   324  	if fsys.OverlayFile != "" {
   325  		for _, filename := range a.Package.AllFiles() {
   326  			path := filename
   327  			if !filepath.IsAbs(path) {
   328  				path = filepath.Join(a.Package.Dir, path)
   329  			}
   330  			base := filepath.Base(path)
   331  			isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s")
   332  			isCgo := cgoFiles[filename] || !isGo
   333  			overlayPath, isOverlay := fsys.OverlayPath(path)
   334  			if isCgo && isOverlay {
   335  				hasCgoOverlay = true
   336  			}
   337  			if !isCgo && isOverlay {
   338  				rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";"
   339  			} else if isCgo {
   340  				// Generate rewrites for non-Go files copied to files in objdir.
   341  				if filepath.Dir(path) == a.Package.Dir {
   342  					// This is a file copied to objdir.
   343  					overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";"
   344  				}
   345  			} else {
   346  				// Non-overlay Go files are covered by the a.Package.Dir rewrite rule above.
   347  			}
   348  		}
   349  	}
   350  	if hasCgoOverlay {
   351  		rewrite += overlayNonGoRewrites
   352  	}
   353  	rewrite += objdir + "=>"
   354  
   355  	return rewrite
   356  }
   357  
   358  func asmArgs(a *Action, p *load.Package) []any {
   359  	// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
   360  	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
   361  	pkgpath := pkgPath(a)
   362  	args := []any{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
   363  	if p.ImportPath == "runtime" && cfg.Goarch == "386" {
   364  		for _, arg := range forcedAsmflags {
   365  			if arg == "-dynlink" {
   366  				args = append(args, "-D=GOBUILDMODE_shared=1")
   367  			}
   368  		}
   369  	}
   370  	if objabi.IsRuntimePackagePath(pkgpath) {
   371  		args = append(args, "-compiling-runtime")
   372  	}
   373  
   374  	if cfg.Goarch == "386" {
   375  		// Define GO386_value from cfg.GO386.
   376  		args = append(args, "-D", "GO386_"+cfg.GO386)
   377  	}
   378  
   379  	if cfg.Goarch == "amd64" {
   380  		// Define GOAMD64_value from cfg.GOAMD64.
   381  		args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64)
   382  	}
   383  
   384  	if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
   385  		// Define GOMIPS_value from cfg.GOMIPS.
   386  		args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
   387  	}
   388  
   389  	if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" {
   390  		// Define GOMIPS64_value from cfg.GOMIPS64.
   391  		args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64)
   392  	}
   393  
   394  	if cfg.Goarch == "ppc64" || cfg.Goarch == "ppc64le" {
   395  		// Define GOPPC64_power8..N from cfg.PPC64.
   396  		// We treat each powerpc version as a superset of functionality.
   397  		switch cfg.GOPPC64 {
   398  		case "power10":
   399  			args = append(args, "-D", "GOPPC64_power10")
   400  			fallthrough
   401  		case "power9":
   402  			args = append(args, "-D", "GOPPC64_power9")
   403  			fallthrough
   404  		default: // This should always be power8.
   405  			args = append(args, "-D", "GOPPC64_power8")
   406  		}
   407  	}
   408  
   409  	return args
   410  }
   411  
   412  func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
   413  	p := a.Package
   414  	args := asmArgs(a, p)
   415  
   416  	var ofiles []string
   417  	for _, sfile := range sfiles {
   418  		overlayPath, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
   419  		ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
   420  		ofiles = append(ofiles, ofile)
   421  		args1 := append(args, "-o", ofile, overlayPath)
   422  		if err := b.run(a, p.Dir, p.ImportPath, nil, args1...); err != nil {
   423  			return nil, err
   424  		}
   425  	}
   426  	return ofiles, nil
   427  }
   428  
   429  func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
   430  	mkSymabis := func(p *load.Package, sfiles []string, path string) error {
   431  		args := asmArgs(a, p)
   432  		args = append(args, "-gensymabis", "-o", path)
   433  		for _, sfile := range sfiles {
   434  			if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
   435  				continue
   436  			}
   437  			op, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
   438  			args = append(args, op)
   439  		}
   440  
   441  		// Supply an empty go_asm.h as if the compiler had been run.
   442  		// -gensymabis parsing is lax enough that we don't need the
   443  		// actual definitions that would appear in go_asm.h.
   444  		if err := b.writeFile(a.Objdir+"go_asm.h", nil); err != nil {
   445  			return err
   446  		}
   447  
   448  		return b.run(a, p.Dir, p.ImportPath, nil, args...)
   449  	}
   450  
   451  	var symabis string // Only set if we actually create the file
   452  	p := a.Package
   453  	if len(sfiles) != 0 {
   454  		symabis = a.Objdir + "symabis"
   455  		if err := mkSymabis(p, sfiles, symabis); err != nil {
   456  			return "", err
   457  		}
   458  	}
   459  
   460  	return symabis, nil
   461  }
   462  
   463  // toolVerify checks that the command line args writes the same output file
   464  // if run using newTool instead.
   465  // Unused now but kept around for future use.
   466  func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error {
   467  	newArgs := make([]any, len(args))
   468  	copy(newArgs, args)
   469  	newArgs[1] = base.Tool(newTool)
   470  	newArgs[3] = ofile + ".new" // x.6 becomes x.6.new
   471  	if err := b.run(a, p.Dir, p.ImportPath, nil, newArgs...); err != nil {
   472  		return err
   473  	}
   474  	data1, err := os.ReadFile(ofile)
   475  	if err != nil {
   476  		return err
   477  	}
   478  	data2, err := os.ReadFile(ofile + ".new")
   479  	if err != nil {
   480  		return err
   481  	}
   482  	if !bytes.Equal(data1, data2) {
   483  		return fmt.Errorf("%s and %s produced different output files:\n%s\n%s", filepath.Base(args[1].(string)), newTool, strings.Join(str.StringList(args...), " "), strings.Join(str.StringList(newArgs...), " "))
   484  	}
   485  	os.Remove(ofile + ".new")
   486  	return nil
   487  }
   488  
   489  func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
   490  	var absOfiles []string
   491  	for _, f := range ofiles {
   492  		absOfiles = append(absOfiles, mkAbs(a.Objdir, f))
   493  	}
   494  	absAfile := mkAbs(a.Objdir, afile)
   495  
   496  	// The archive file should have been created by the compiler.
   497  	// Since it used to not work that way, verify.
   498  	if !cfg.BuildN {
   499  		if _, err := os.Stat(absAfile); err != nil {
   500  			base.Fatalf("os.Stat of archive file failed: %v", err)
   501  		}
   502  	}
   503  
   504  	p := a.Package
   505  	if cfg.BuildN || cfg.BuildX {
   506  		cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles)
   507  		b.Showcmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
   508  	}
   509  	if cfg.BuildN {
   510  		return nil
   511  	}
   512  	if err := packInternal(absAfile, absOfiles); err != nil {
   513  		return formatOutput(b.WorkDir, p.Dir, p.ImportPath, p.Desc(), err.Error()+"\n")
   514  	}
   515  	return nil
   516  }
   517  
   518  func packInternal(afile string, ofiles []string) error {
   519  	dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0)
   520  	if err != nil {
   521  		return err
   522  	}
   523  	defer dst.Close() // only for error returns or panics
   524  	w := bufio.NewWriter(dst)
   525  
   526  	for _, ofile := range ofiles {
   527  		src, err := os.Open(ofile)
   528  		if err != nil {
   529  			return err
   530  		}
   531  		fi, err := src.Stat()
   532  		if err != nil {
   533  			src.Close()
   534  			return err
   535  		}
   536  		// Note: Not using %-16.16s format because we care
   537  		// about bytes, not runes.
   538  		name := fi.Name()
   539  		if len(name) > 16 {
   540  			name = name[:16]
   541  		} else {
   542  			name += strings.Repeat(" ", 16-len(name))
   543  		}
   544  		size := fi.Size()
   545  		fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n",
   546  			name, 0, 0, 0, 0644, size)
   547  		n, err := io.Copy(w, src)
   548  		src.Close()
   549  		if err == nil && n < size {
   550  			err = io.ErrUnexpectedEOF
   551  		} else if err == nil && n > size {
   552  			err = fmt.Errorf("file larger than size reported by stat")
   553  		}
   554  		if err != nil {
   555  			return fmt.Errorf("copying %s to %s: %v", ofile, afile, err)
   556  		}
   557  		if size&1 != 0 {
   558  			w.WriteByte(0)
   559  		}
   560  	}
   561  
   562  	if err := w.Flush(); err != nil {
   563  		return err
   564  	}
   565  	return dst.Close()
   566  }
   567  
   568  // setextld sets the appropriate linker flags for the specified compiler.
   569  func setextld(ldflags []string, compiler []string) ([]string, error) {
   570  	for _, f := range ldflags {
   571  		if f == "-extld" || strings.HasPrefix(f, "-extld=") {
   572  			// don't override -extld if supplied
   573  			return ldflags, nil
   574  		}
   575  	}
   576  	joined, err := quoted.Join(compiler)
   577  	if err != nil {
   578  		return nil, err
   579  	}
   580  	return append(ldflags, "-extld="+joined), nil
   581  }
   582  
   583  // pluginPath computes the package path for a plugin main package.
   584  //
   585  // This is typically the import path of the main package p, unless the
   586  // plugin is being built directly from source files. In that case we
   587  // combine the package build ID with the contents of the main package
   588  // source files. This allows us to identify two different plugins
   589  // built from two source files with the same name.
   590  func pluginPath(a *Action) string {
   591  	p := a.Package
   592  	if p.ImportPath != "command-line-arguments" {
   593  		return p.ImportPath
   594  	}
   595  	h := sha1.New()
   596  	buildID := a.buildID
   597  	if a.Mode == "link" {
   598  		// For linking, use the main package's build ID instead of
   599  		// the binary's build ID, so it is the same hash used in
   600  		// compiling and linking.
   601  		// When compiling, we use actionID/actionID (instead of
   602  		// actionID/contentID) as a temporary build ID to compute
   603  		// the hash. Do the same here. (See buildid.go:useCache)
   604  		// The build ID matters because it affects the overall hash
   605  		// in the plugin's pseudo-import path returned below.
   606  		// We need to use the same import path when compiling and linking.
   607  		id := strings.Split(buildID, buildIDSeparator)
   608  		buildID = id[1] + buildIDSeparator + id[1]
   609  	}
   610  	fmt.Fprintf(h, "build ID: %s\n", buildID)
   611  	for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) {
   612  		data, err := os.ReadFile(filepath.Join(p.Dir, file))
   613  		if err != nil {
   614  			base.Fatalf("go: %s", err)
   615  		}
   616  		h.Write(data)
   617  	}
   618  	return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil))
   619  }
   620  
   621  func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
   622  	cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
   623  	for _, a := range root.Deps {
   624  		if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
   625  			cxx = true
   626  		}
   627  	}
   628  	var ldflags []string
   629  	if cfg.BuildContext.InstallSuffix != "" {
   630  		ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix)
   631  	}
   632  	if root.Package.Internal.OmitDebug {
   633  		ldflags = append(ldflags, "-s", "-w")
   634  	}
   635  	if cfg.BuildBuildmode == "plugin" {
   636  		ldflags = append(ldflags, "-pluginpath", pluginPath(root))
   637  	}
   638  
   639  	// Store BuildID inside toolchain binaries as a unique identifier of the
   640  	// tool being run, for use by content-based staleness determination.
   641  	if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {
   642  		// External linking will include our build id in the external
   643  		// linker's build id, which will cause our build id to not
   644  		// match the next time the tool is built.
   645  		// Rely on the external build id instead.
   646  		if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch) {
   647  			ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
   648  		}
   649  	}
   650  
   651  	// If the user has not specified the -extld option, then specify the
   652  	// appropriate linker. In case of C++ code, use the compiler named
   653  	// by the CXX environment variable or defaultCXX if CXX is not set.
   654  	// Else, use the CC environment variable and defaultCC as fallback.
   655  	var compiler []string
   656  	if cxx {
   657  		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
   658  	} else {
   659  		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   660  	}
   661  	ldflags = append(ldflags, "-buildmode="+ldBuildmode)
   662  	if root.buildID != "" {
   663  		ldflags = append(ldflags, "-buildid="+root.buildID)
   664  	}
   665  	ldflags = append(ldflags, forcedLdflags...)
   666  	ldflags = append(ldflags, root.Package.Internal.Ldflags...)
   667  	ldflags, err := setextld(ldflags, compiler)
   668  	if err != nil {
   669  		return err
   670  	}
   671  
   672  	// On OS X when using external linking to build a shared library,
   673  	// the argument passed here to -o ends up recorded in the final
   674  	// shared library in the LC_ID_DYLIB load command.
   675  	// To avoid putting the temporary output directory name there
   676  	// (and making the resulting shared library useless),
   677  	// run the link in the output directory so that -o can name
   678  	// just the final path element.
   679  	// On Windows, DLL file name is recorded in PE file
   680  	// export section, so do like on OS X.
   681  	dir := "."
   682  	if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" {
   683  		dir, out = filepath.Split(out)
   684  	}
   685  
   686  	env := []string{}
   687  	if cfg.BuildTrimpath {
   688  		env = append(env, "GOROOT_FINAL="+trimPathGoRootFinal)
   689  	}
   690  	return b.run(root, dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags, mainpkg)
   691  }
   692  
   693  func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
   694  	ldflags := []string{"-installsuffix", cfg.BuildContext.InstallSuffix}
   695  	ldflags = append(ldflags, "-buildmode=shared")
   696  	ldflags = append(ldflags, forcedLdflags...)
   697  	ldflags = append(ldflags, root.Package.Internal.Ldflags...)
   698  	cxx := false
   699  	for _, a := range allactions {
   700  		if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
   701  			cxx = true
   702  		}
   703  	}
   704  	// If the user has not specified the -extld option, then specify the
   705  	// appropriate linker. In case of C++ code, use the compiler named
   706  	// by the CXX environment variable or defaultCXX if CXX is not set.
   707  	// Else, use the CC environment variable and defaultCC as fallback.
   708  	var compiler []string
   709  	if cxx {
   710  		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
   711  	} else {
   712  		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   713  	}
   714  	ldflags, err := setextld(ldflags, compiler)
   715  	if err != nil {
   716  		return err
   717  	}
   718  	for _, d := range toplevelactions {
   719  		if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries
   720  			continue
   721  		}
   722  		ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
   723  	}
   724  	return b.run(root, ".", out, nil, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags)
   725  }
   726  
   727  func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
   728  	return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile))
   729  }