1
2
3
4
5 package work
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "internal/buildcfg"
12 "internal/platform"
13 "io"
14 "log"
15 "os"
16 "path/filepath"
17 "runtime"
18 "strings"
19
20 "cmd/go/internal/base"
21 "cmd/go/internal/cfg"
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
31 var ToolchainVersion = runtime.Version()
32
33
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
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
86
87
88
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
130 if c := gcBackendConcurrency(gcflags); c > 1 {
131 defaultGcFlags = append(defaultGcFlags, fmt.Sprintf("-c=%d", c))
132 }
133
134 args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags}
135 if p.Internal.LocalPrefix == "" {
136 args = append(args, "-nolocalimports")
137 } else {
138 args = append(args, "-D", p.Internal.LocalPrefix)
139 }
140 if importcfg != nil {
141 if err := sh.writeFile(objdir+"importcfg", importcfg); err != nil {
142 return "", nil, err
143 }
144 args = append(args, "-importcfg", objdir+"importcfg")
145 }
146 if embedcfg != nil {
147 if err := sh.writeFile(objdir+"embedcfg", embedcfg); err != nil {
148 return "", nil, err
149 }
150 args = append(args, "-embedcfg", objdir+"embedcfg")
151 }
152 if ofile == archive {
153 args = append(args, "-pack")
154 }
155 if asmhdr {
156 args = append(args, "-asmhdr", objdir+"go_asm.h")
157 }
158
159 for _, f := range gofiles {
160 f := mkAbs(p.Dir, f)
161
162
163
164
165
166
167
168
169
170
171
172
173
174 f, _ = fsys.OverlayPath(f)
175
176 args = append(args, f)
177 }
178
179 output, err = sh.runOut(base.Cwd(), nil, args...)
180 return ofile, output, err
181 }
182
183
184 func gcBackendConcurrency(gcflags []string) int {
185
186 canDashC := concurrentGCBackendCompilationEnabledByDefault
187
188 switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
189 case "0":
190 canDashC = false
191 case "1":
192 canDashC = true
193 case "":
194
195 default:
196 log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
197 }
198
199
200 if cfg.ExperimentErr != nil || cfg.Experiment.FieldTrack || cfg.Experiment.PreemptibleLoops {
201 canDashC = false
202 }
203
204 if !canDashC {
205 return 1
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 c := runtime.GOMAXPROCS(0)
232 if cfg.BuildP == 1 {
233
234 return c
235 }
236
237 if c > 4 {
238 c = 4
239 }
240 return c
241 }
242
243
244
245 func (a *Action) trimpath() string {
246
247
248
249
250
251 objdir := strings.TrimSuffix(a.Objdir, string(filepath.Separator))
252 rewrite := ""
253
254 rewriteDir := a.Package.Dir
255 if cfg.BuildTrimpath {
256 importPath := a.Package.Internal.OrigImportPath
257 if m := a.Package.Module; m != nil && m.Version != "" {
258 rewriteDir = m.Path + "@" + m.Version + strings.TrimPrefix(importPath, m.Path)
259 } else {
260 rewriteDir = importPath
261 }
262 rewrite += a.Package.Dir + "=>" + rewriteDir + ";"
263 }
264
265
266
267
268
269 cgoFiles := make(map[string]bool)
270 for _, f := range a.Package.CgoFiles {
271 cgoFiles[f] = true
272 }
273
274
275
276
277
278 var overlayNonGoRewrites string
279 hasCgoOverlay := false
280 if fsys.OverlayFile != "" {
281 for _, filename := range a.Package.AllFiles() {
282 path := filename
283 if !filepath.IsAbs(path) {
284 path = filepath.Join(a.Package.Dir, path)
285 }
286 base := filepath.Base(path)
287 isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s")
288 isCgo := cgoFiles[filename] || !isGo
289 overlayPath, isOverlay := fsys.OverlayPath(path)
290 if isCgo && isOverlay {
291 hasCgoOverlay = true
292 }
293 if !isCgo && isOverlay {
294 rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";"
295 } else if isCgo {
296
297 if filepath.Dir(path) == a.Package.Dir {
298
299 overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";"
300 }
301 } else {
302
303 }
304 }
305 }
306 if hasCgoOverlay {
307 rewrite += overlayNonGoRewrites
308 }
309 rewrite += objdir + "=>"
310
311 return rewrite
312 }
313
314 func asmArgs(a *Action, p *load.Package) []any {
315
316 inc := filepath.Join(cfg.GOROOT, "pkg", "include")
317 pkgpath := pkgPath(a)
318 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}
319 if p.ImportPath == "runtime" && cfg.Goarch == "386" {
320 for _, arg := range forcedAsmflags {
321 if arg == "-dynlink" {
322 args = append(args, "-D=GOBUILDMODE_shared=1")
323 }
324 }
325 }
326
327 if cfg.Goarch == "386" {
328
329 args = append(args, "-D", "GO386_"+cfg.GO386)
330 }
331
332 if cfg.Goarch == "amd64" {
333
334 args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64)
335 }
336
337 if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
338
339 args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
340 }
341
342 if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" {
343
344 args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64)
345 }
346
347 if cfg.Goarch == "ppc64" || cfg.Goarch == "ppc64le" {
348
349
350 switch cfg.GOPPC64 {
351 case "power10":
352 args = append(args, "-D", "GOPPC64_power10")
353 fallthrough
354 case "power9":
355 args = append(args, "-D", "GOPPC64_power9")
356 fallthrough
357 default:
358 args = append(args, "-D", "GOPPC64_power8")
359 }
360 }
361
362 if cfg.Goarch == "riscv64" {
363
364 args = append(args, "-D", "GORISCV64_"+cfg.GORISCV64)
365 }
366
367 if cfg.Goarch == "arm" {
368
369
370 switch {
371 case strings.Contains(cfg.GOARM, "7"):
372 args = append(args, "-D", "GOARM_7")
373 fallthrough
374 case strings.Contains(cfg.GOARM, "6"):
375 args = append(args, "-D", "GOARM_6")
376 fallthrough
377 default:
378 args = append(args, "-D", "GOARM_5")
379 }
380 }
381
382 if cfg.Goarch == "arm64" {
383 g, err := buildcfg.ParseGoarm64(cfg.GOARM64)
384 if err == nil && g.LSE {
385 args = append(args, "-D", "GOARM64_LSE")
386 }
387 }
388
389 return args
390 }
391
392 func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
393 p := a.Package
394 args := asmArgs(a, p)
395
396 var ofiles []string
397 for _, sfile := range sfiles {
398 overlayPath, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
399 ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
400 ofiles = append(ofiles, ofile)
401 args1 := append(args, "-o", ofile, overlayPath)
402 if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, args1...); err != nil {
403 return nil, err
404 }
405 }
406 return ofiles, nil
407 }
408
409 func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
410 sh := b.Shell(a)
411
412 mkSymabis := func(p *load.Package, sfiles []string, path string) error {
413 args := asmArgs(a, p)
414 args = append(args, "-gensymabis", "-o", path)
415 for _, sfile := range sfiles {
416 if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
417 continue
418 }
419 op, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile))
420 args = append(args, op)
421 }
422
423
424
425
426 if err := sh.writeFile(a.Objdir+"go_asm.h", nil); err != nil {
427 return err
428 }
429
430 return sh.run(p.Dir, p.ImportPath, nil, args...)
431 }
432
433 var symabis string
434 p := a.Package
435 if len(sfiles) != 0 {
436 symabis = a.Objdir + "symabis"
437 if err := mkSymabis(p, sfiles, symabis); err != nil {
438 return "", err
439 }
440 }
441
442 return symabis, nil
443 }
444
445
446
447
448 func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error {
449 newArgs := make([]any, len(args))
450 copy(newArgs, args)
451 newArgs[1] = base.Tool(newTool)
452 newArgs[3] = ofile + ".new"
453 if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, newArgs...); err != nil {
454 return err
455 }
456 data1, err := os.ReadFile(ofile)
457 if err != nil {
458 return err
459 }
460 data2, err := os.ReadFile(ofile + ".new")
461 if err != nil {
462 return err
463 }
464 if !bytes.Equal(data1, data2) {
465 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...), " "))
466 }
467 os.Remove(ofile + ".new")
468 return nil
469 }
470
471 func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
472 absOfiles := make([]string, 0, len(ofiles))
473 for _, f := range ofiles {
474 absOfiles = append(absOfiles, mkAbs(a.Objdir, f))
475 }
476 absAfile := mkAbs(a.Objdir, afile)
477
478
479
480 if !cfg.BuildN {
481 if _, err := os.Stat(absAfile); err != nil {
482 base.Fatalf("os.Stat of archive file failed: %v", err)
483 }
484 }
485
486 p := a.Package
487 sh := b.Shell(a)
488 if cfg.BuildN || cfg.BuildX {
489 cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles)
490 sh.ShowCmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
491 }
492 if cfg.BuildN {
493 return nil
494 }
495 if err := packInternal(absAfile, absOfiles); err != nil {
496 return sh.reportCmd("", "", nil, err)
497 }
498 return nil
499 }
500
501 func packInternal(afile string, ofiles []string) error {
502 dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0)
503 if err != nil {
504 return err
505 }
506 defer dst.Close()
507 w := bufio.NewWriter(dst)
508
509 for _, ofile := range ofiles {
510 src, err := os.Open(ofile)
511 if err != nil {
512 return err
513 }
514 fi, err := src.Stat()
515 if err != nil {
516 src.Close()
517 return err
518 }
519
520
521 name := fi.Name()
522 if len(name) > 16 {
523 name = name[:16]
524 } else {
525 name += strings.Repeat(" ", 16-len(name))
526 }
527 size := fi.Size()
528 fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n",
529 name, 0, 0, 0, 0644, size)
530 n, err := io.Copy(w, src)
531 src.Close()
532 if err == nil && n < size {
533 err = io.ErrUnexpectedEOF
534 } else if err == nil && n > size {
535 err = fmt.Errorf("file larger than size reported by stat")
536 }
537 if err != nil {
538 return fmt.Errorf("copying %s to %s: %v", ofile, afile, err)
539 }
540 if size&1 != 0 {
541 w.WriteByte(0)
542 }
543 }
544
545 if err := w.Flush(); err != nil {
546 return err
547 }
548 return dst.Close()
549 }
550
551
552 func setextld(ldflags []string, compiler []string) ([]string, error) {
553 for _, f := range ldflags {
554 if f == "-extld" || strings.HasPrefix(f, "-extld=") {
555
556 return ldflags, nil
557 }
558 }
559 joined, err := quoted.Join(compiler)
560 if err != nil {
561 return nil, err
562 }
563 return append(ldflags, "-extld="+joined), nil
564 }
565
566
567
568
569
570
571
572
573 func pluginPath(a *Action) string {
574 p := a.Package
575 if p.ImportPath != "command-line-arguments" {
576 return p.ImportPath
577 }
578 h := sha1.New()
579 buildID := a.buildID
580 if a.Mode == "link" {
581
582
583
584
585
586
587
588
589
590 id := strings.Split(buildID, buildIDSeparator)
591 buildID = id[1] + buildIDSeparator + id[1]
592 }
593 fmt.Fprintf(h, "build ID: %s\n", buildID)
594 for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) {
595 data, err := os.ReadFile(filepath.Join(p.Dir, file))
596 if err != nil {
597 base.Fatalf("go: %s", err)
598 }
599 h.Write(data)
600 }
601 return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil))
602 }
603
604 func (gcToolchain) ld(b *Builder, root *Action, targetPath, importcfg, mainpkg string) error {
605 cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
606 for _, a := range root.Deps {
607 if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
608 cxx = true
609 }
610 }
611 var ldflags []string
612 if cfg.BuildContext.InstallSuffix != "" {
613 ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix)
614 }
615 if root.Package.Internal.OmitDebug {
616 ldflags = append(ldflags, "-s", "-w")
617 }
618 if cfg.BuildBuildmode == "plugin" {
619 ldflags = append(ldflags, "-pluginpath", pluginPath(root))
620 }
621
622
623
624 if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {
625
626
627
628
629 if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch, false) {
630 ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
631 }
632 }
633
634
635 if root.Package.DefaultGODEBUG != "" {
636 ldflags = append(ldflags, "-X=runtime.godebugDefault="+root.Package.DefaultGODEBUG)
637 }
638
639
640
641
642
643 var compiler []string
644 if cxx {
645 compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
646 } else {
647 compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
648 }
649 ldflags = append(ldflags, "-buildmode="+ldBuildmode)
650 if root.buildID != "" {
651 ldflags = append(ldflags, "-buildid="+root.buildID)
652 }
653 ldflags = append(ldflags, forcedLdflags...)
654 ldflags = append(ldflags, root.Package.Internal.Ldflags...)
655 ldflags, err := setextld(ldflags, compiler)
656 if err != nil {
657 return err
658 }
659
660
661
662
663
664
665
666
667
668
669
670
671 dir := "."
672 if cfg.BuildBuildmode == "c-shared" || cfg.BuildBuildmode == "plugin" {
673 dir, targetPath = filepath.Split(targetPath)
674 }
675
676 env := []string{}
677
678 if cfg.BuildTrimpath {
679 env = append(env, "GOROOT=")
680 } else {
681 env = append(env, "GOROOT="+cfg.GOROOT)
682 }
683 return b.Shell(root).run(dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags, mainpkg)
684 }
685
686 func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, targetPath, 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
698
699
700
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") {
713 continue
714 }
715 ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
716 }
717
718
719
720
721
722
723
724
725
726
727
728
729 dir, targetPath := filepath.Split(targetPath)
730
731 return b.Shell(root).run(dir, targetPath, nil, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags)
732 }
733
734 func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
735 return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile))
736 }
737
View as plain text