1
2
3
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
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 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
165
166
167
168
169
170
171
172
173
174
175
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
183
184 func compilerConcurrency() (int, func()) {
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 if cfg.BuildP == 1 {
210
211 return maxCompilerConcurrency, func() {}
212 }
213
214
215 tokensMu.Lock()
216 defer tokensMu.Unlock()
217 concurrentProcesses++
218
219
220 setAside := (cfg.BuildP - concurrentProcesses) * minTokens
221 availableTokens := tokens - setAside
222
223
224 c := max(min(availableTokens/2, maxCompilerConcurrency), minTokens)
225 tokens -= c
226
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)
236
237 var (
238 tokensMu sync.Mutex
239 totalTokens int
240 tokens int
241 concurrentProcesses int
242 minTokens int
243 )
244
245
246
247 func initCompilerConcurrencyPool() {
248
249
250
251
252 minTokens = min(4, maxCompilerConcurrency)
253 tokens = 2*maxCompilerConcurrency + minTokens*cfg.BuildP
254 totalTokens = tokens
255 }
256
257
258
259 func (a *Action) trimpath() string {
260
261
262
263
264
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
280
281
282
283 cgoFiles := make(map[string]bool)
284 for _, f := range a.Package.CgoFiles {
285 cgoFiles[f] = true
286 }
287
288
289
290
291
292 var overlayNonGoRewrites string
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
311 if filepath.Dir(path) == a.Package.Dir {
312
313 overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";"
314 }
315 } else {
316
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
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
347 args = append(args, "-D", "GO386_"+cfg.GO386)
348 }
349
350 if cfg.Goarch == "amd64" {
351
352 args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64)
353 }
354
355 if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
356
357 args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
358 }
359
360 if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" {
361
362 args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64)
363 }
364
365 if cfg.Goarch == "ppc64" || cfg.Goarch == "ppc64le" {
366
367
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:
376 args = append(args, "-D", "GOPPC64_power8")
377 }
378 }
379
380 if cfg.Goarch == "riscv64" {
381
382 args = append(args, "-D", "GORISCV64_"+cfg.GORISCV64)
383 }
384
385 if cfg.Goarch == "arm" {
386
387
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
440
441
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
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
469
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()
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
510
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
542 func setextld(ldflags []string, compiler []string) ([]string, error) {
543 for _, f := range ldflags {
544 if f == "-extld" || strings.HasPrefix(f, "-extld=") {
545
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
557
558
559
560
561
562
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
572
573
574
575
576
577
578
579
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
616
617 if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") {
618
619
620
621
622 if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch, false) {
623 ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
624 }
625 }
626
627
628 if root.Package.DefaultGODEBUG != "" {
629 ldflags = append(ldflags, "-X=runtime.godebugDefault="+root.Package.DefaultGODEBUG)
630 }
631
632
633
634
635
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
654
655
656
657
658
659
660
661
662
663
664 dir := "."
665 if cfg.BuildBuildmode == "c-shared" || cfg.BuildBuildmode == "plugin" {
666 dir, targetPath = filepath.Split(targetPath)
667 }
668
669 env := cfgChangedEnv
670
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
691
692
693
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") {
706 continue
707 }
708 ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
709 }
710
711
712
713
714
715
716
717
718
719
720
721
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