Source file src/cmd/go/internal/work/gc.go

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

View as plain text