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