]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/cmd/go/internal/work/gccgo.go
cmd/go: permit $AR to include options
[thirdparty/gcc.git] / libgo / go / cmd / go / internal / work / gccgo.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 "fmt"
9 exec "internal/execabs"
10 "os"
11 "path/filepath"
12 "strings"
13 "sync"
14
15 "cmd/go/internal/base"
16 "cmd/go/internal/cfg"
17 "cmd/go/internal/fsys"
18 "cmd/go/internal/load"
19 "cmd/go/internal/str"
20 "cmd/internal/pkgpath"
21 )
22
23 // The Gccgo toolchain.
24
25 type gccgoToolchain struct{}
26
27 var GccgoName, GccgoBin string
28 var gccgoErr error
29
30 func init() {
31 GccgoName = cfg.Getenv("GCCGO")
32 if GccgoName == "" {
33 GccgoName = cfg.DefaultGCCGO(cfg.Goos, cfg.Goarch)
34 }
35 GccgoBin, gccgoErr = exec.LookPath(GccgoName)
36 }
37
38 func (gccgoToolchain) compiler() string {
39 checkGccgoBin()
40 return GccgoBin
41 }
42
43 func (gccgoToolchain) linker() string {
44 checkGccgoBin()
45 return GccgoBin
46 }
47
48 func (gccgoToolchain) ar() []string {
49 return envList("AR", "ar")
50 }
51
52 func checkGccgoBin() {
53 if gccgoErr == nil {
54 return
55 }
56 fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
57 base.SetExitStatus(2)
58 base.Exit()
59 }
60
61 func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
62 p := a.Package
63 objdir := a.Objdir
64 out := "_go_.o"
65 ofile = objdir + out
66 gcargs := []string{"-g"}
67 gcargs = append(gcargs, b.gccArchArgs()...)
68 gcargs = append(gcargs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
69 gcargs = append(gcargs, "-gno-record-gcc-switches")
70 if pkgpath := gccgoPkgpath(p); pkgpath != "" {
71 gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
72 }
73 if p.Internal.LocalPrefix != "" {
74 gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
75 }
76
77 args := str.StringList(tools.compiler(), "-c", "-O2", gcargs, "-o", ofile, forcedGccgoflags)
78 if importcfg != nil {
79 if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
80 if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
81 return "", nil, err
82 }
83 args = append(args, "-fgo-importcfg="+objdir+"importcfg")
84 } else {
85 root := objdir + "_importcfgroot_"
86 if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
87 return "", nil, err
88 }
89 args = append(args, "-I", root)
90 }
91 }
92 if embedcfg != nil && b.gccSupportsFlag(args[:1], "-fgo-embedcfg=/dev/null") {
93 if err := b.writeFile(objdir+"embedcfg", embedcfg); err != nil {
94 return "", nil, err
95 }
96 args = append(args, "-fgo-embedcfg="+objdir+"embedcfg")
97 }
98
99 if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
100 if cfg.BuildTrimpath {
101 args = append(args, "-ffile-prefix-map="+base.Cwd()+"=.")
102 args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
103 }
104 if fsys.OverlayFile != "" {
105 for _, name := range gofiles {
106 absPath := mkAbs(p.Dir, name)
107 overlayPath, ok := fsys.OverlayPath(absPath)
108 if !ok {
109 continue
110 }
111 toPath := absPath
112 // gccgo only applies the last matching rule, so also handle the case where
113 // BuildTrimpath is true and the path is relative to base.Cwd().
114 if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd()) {
115 toPath = "." + toPath[len(base.Cwd()):]
116 }
117 args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath)
118 }
119 }
120 }
121
122 args = append(args, a.Package.Internal.Gccgoflags...)
123 for _, f := range gofiles {
124 f := mkAbs(p.Dir, f)
125 // Overlay files if necessary.
126 // See comment on gctoolchain.gc about overlay TODOs
127 f, _ = fsys.OverlayPath(f)
128 args = append(args, f)
129 }
130
131 output, err = b.runOut(a, p.Dir, nil, args)
132 return ofile, output, err
133 }
134
135 // buildImportcfgSymlinks builds in root a tree of symlinks
136 // implementing the directives from importcfg.
137 // This serves as a temporary transition mechanism until
138 // we can depend on gccgo reading an importcfg directly.
139 // (The Go 1.9 and later gc compilers already do.)
140 func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
141 for lineNum, line := range strings.Split(string(importcfg), "\n") {
142 lineNum++ // 1-based
143 line = strings.TrimSpace(line)
144 if line == "" {
145 continue
146 }
147 if line == "" || strings.HasPrefix(line, "#") {
148 continue
149 }
150 var verb, args string
151 if i := strings.Index(line, " "); i < 0 {
152 verb = line
153 } else {
154 verb, args = line[:i], strings.TrimSpace(line[i+1:])
155 }
156 var before, after string
157 if i := strings.Index(args, "="); i >= 0 {
158 before, after = args[:i], args[i+1:]
159 }
160 switch verb {
161 default:
162 base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
163 case "packagefile":
164 if before == "" || after == "" {
165 return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
166 }
167 archive := gccgoArchive(root, before)
168 if err := b.Mkdir(filepath.Dir(archive)); err != nil {
169 return err
170 }
171 if err := b.Symlink(after, archive); err != nil {
172 return err
173 }
174 case "importmap":
175 if before == "" || after == "" {
176 return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
177 }
178 beforeA := gccgoArchive(root, before)
179 afterA := gccgoArchive(root, after)
180 if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
181 return err
182 }
183 if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
184 return err
185 }
186 if err := b.Symlink(afterA, beforeA); err != nil {
187 return err
188 }
189 case "packageshlib":
190 return fmt.Errorf("gccgo -importcfg does not support shared libraries")
191 }
192 }
193 return nil
194 }
195
196 func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
197 p := a.Package
198 var ofiles []string
199 for _, sfile := range sfiles {
200 base := filepath.Base(sfile)
201 ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
202 ofiles = append(ofiles, ofile)
203 sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile))
204 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
205 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
206 defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
207 }
208 defs = tools.maybePIC(defs)
209 defs = append(defs, b.gccArchArgs()...)
210 err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
211 if err != nil {
212 return nil, err
213 }
214 }
215 return ofiles, nil
216 }
217
218 func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
219 return "", nil
220 }
221
222 func gccgoArchive(basedir, imp string) string {
223 end := filepath.FromSlash(imp + ".a")
224 afile := filepath.Join(basedir, end)
225 // add "lib" to the final element
226 return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
227 }
228
229 func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
230 p := a.Package
231 objdir := a.Objdir
232 var absOfiles []string
233 for _, f := range ofiles {
234 absOfiles = append(absOfiles, mkAbs(objdir, f))
235 }
236 var arArgs []string
237 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
238 // AIX puts both 32-bit and 64-bit objects in the same archive.
239 // Tell the AIX "ar" command to only care about 64-bit objects.
240 arArgs = []string{"-X64"}
241 }
242 absAfile := mkAbs(objdir, afile)
243 // Try with D modifier first, then without if that fails.
244 output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
245 if err != nil {
246 return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
247 }
248
249 if len(output) > 0 {
250 // Show the output if there is any even without errors.
251 b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output))
252 }
253
254 return nil
255 }
256
257 func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
258 // gccgo needs explicit linking with all package dependencies,
259 // and all LDFLAGS from cgo dependencies.
260 afiles := []string{}
261 shlibs := []string{}
262 ldflags := b.gccArchArgs()
263 cgoldflags := []string{}
264 usesCgo := false
265 cxx := false
266 objc := false
267 fortran := false
268 if root.Package != nil {
269 cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
270 objc = len(root.Package.MFiles) > 0
271 fortran = len(root.Package.FFiles) > 0
272 }
273
274 readCgoFlags := func(flagsFile string) error {
275 flags, err := os.ReadFile(flagsFile)
276 if err != nil {
277 return err
278 }
279 const ldflagsPrefix = "_CGO_LDFLAGS="
280 for _, line := range strings.Split(string(flags), "\n") {
281 if strings.HasPrefix(line, ldflagsPrefix) {
282 line = line[len(ldflagsPrefix):]
283 quote := byte(0)
284 start := true
285 var nl []byte
286 for len(line) > 0 {
287 b := line[0]
288 line = line[1:]
289 if quote == 0 && (b == ' ' || b == '\t') {
290 if len(nl) > 0 {
291 cgoldflags = append(cgoldflags, string(nl))
292 nl = nil
293 }
294 start = true
295 continue
296 } else if b == '"' || b == '\'' {
297 quote = b
298 } else if b == quote {
299 quote = 0
300 } else if quote == 0 && start && b == '-' && strings.HasPrefix(line, "g") {
301 line = line[1:]
302 continue
303 } else if quote == 0 && start && b == '-' && strings.HasPrefix(line, "O") {
304 for len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
305 line = line[1:]
306 }
307 continue
308 }
309 nl = append(nl, b)
310 start = false
311 }
312 if len(nl) > 0 {
313 cgoldflags = append(cgoldflags, string(nl))
314 }
315 }
316 }
317 return nil
318 }
319
320 var arArgs []string
321 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
322 // AIX puts both 32-bit and 64-bit objects in the same archive.
323 // Tell the AIX "ar" command to only care about 64-bit objects.
324 arArgs = []string{"-X64"}
325 }
326
327 newID := 0
328 readAndRemoveCgoFlags := func(archive string) (string, error) {
329 newID++
330 newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
331 if err := b.copyFile(newArchive, archive, 0666, false); err != nil {
332 return "", err
333 }
334 if cfg.BuildN {
335 // TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
336 // Either the archive is already built and we can read them out,
337 // or we're printing commands to build the archive and can
338 // forward the _cgo_flags directly to this step.
339 b.Showcmd("", "ar d %s _cgo_flags", newArchive)
340 return "", nil
341 }
342 err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
343 if err != nil {
344 return "", err
345 }
346 err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
347 if err != nil {
348 return "", err
349 }
350 err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
351 if err != nil {
352 return "", err
353 }
354 return newArchive, nil
355 }
356
357 // If using -linkshared, find the shared library deps.
358 haveShlib := make(map[string]bool)
359 targetBase := filepath.Base(root.Target)
360 if cfg.BuildLinkshared {
361 for _, a := range root.Deps {
362 p := a.Package
363 if p == nil || p.Shlib == "" {
364 continue
365 }
366
367 // The .a we are linking into this .so
368 // will have its Shlib set to this .so.
369 // Don't start thinking we want to link
370 // this .so into itself.
371 base := filepath.Base(p.Shlib)
372 if base != targetBase {
373 haveShlib[base] = true
374 }
375 }
376 }
377
378 // Arrange the deps into afiles and shlibs.
379 addedShlib := make(map[string]bool)
380 for _, a := range root.Deps {
381 p := a.Package
382 if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] {
383 // This is a package linked into a shared
384 // library that we will put into shlibs.
385 continue
386 }
387
388 if haveShlib[filepath.Base(a.Target)] {
389 // This is a shared library we want to link against.
390 if !addedShlib[a.Target] {
391 shlibs = append(shlibs, a.Target)
392 addedShlib[a.Target] = true
393 }
394 continue
395 }
396
397 if p != nil {
398 target := a.built
399 if p.UsesCgo() || p.UsesSwig() {
400 var err error
401 target, err = readAndRemoveCgoFlags(target)
402 if err != nil {
403 continue
404 }
405 }
406
407 afiles = append(afiles, target)
408 }
409 }
410
411 for _, a := range allactions {
412 if a.Package == nil {
413 continue
414 }
415 if len(a.Package.CgoFiles) > 0 {
416 usesCgo = true
417 }
418 if a.Package.UsesSwig() {
419 usesCgo = true
420 }
421 if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
422 cxx = true
423 }
424 if len(a.Package.MFiles) > 0 {
425 objc = true
426 }
427 if len(a.Package.FFiles) > 0 {
428 fortran = true
429 }
430 }
431
432 wholeArchive := []string{"-Wl,--whole-archive"}
433 noWholeArchive := []string{"-Wl,--no-whole-archive"}
434 if cfg.Goos == "aix" {
435 wholeArchive = nil
436 noWholeArchive = nil
437 }
438 ldflags = append(ldflags, wholeArchive...)
439 ldflags = append(ldflags, afiles...)
440 ldflags = append(ldflags, noWholeArchive...)
441
442 ldflags = append(ldflags, cgoldflags...)
443 ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
444 if cfg.Goos != "aix" {
445 ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
446 }
447
448 if root.buildID != "" {
449 // On systems that normally use gold or the GNU linker,
450 // use the --build-id option to write a GNU build ID note.
451 switch cfg.Goos {
452 case "android", "dragonfly", "linux", "netbsd":
453 ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
454 }
455 }
456
457 var rLibPath string
458 if cfg.Goos == "aix" {
459 rLibPath = "-Wl,-blibpath="
460 } else {
461 rLibPath = "-Wl,-rpath="
462 }
463 for _, shlib := range shlibs {
464 ldflags = append(
465 ldflags,
466 "-L"+filepath.Dir(shlib),
467 rLibPath+filepath.Dir(shlib),
468 "-l"+strings.TrimSuffix(
469 strings.TrimPrefix(filepath.Base(shlib), "lib"),
470 ".so"))
471 }
472
473 var realOut string
474 goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
475 switch buildmode {
476 case "exe":
477 if usesCgo && cfg.Goos == "linux" {
478 ldflags = append(ldflags, "-Wl,-E")
479 }
480
481 case "c-archive":
482 // Link the Go files into a single .o, and also link
483 // in -lgolibbegin.
484 //
485 // We need to use --whole-archive with -lgolibbegin
486 // because it doesn't define any symbols that will
487 // cause the contents to be pulled in; it's just
488 // initialization code.
489 //
490 // The user remains responsible for linking against
491 // -lgo -lpthread -lm in the final link. We can't use
492 // -r to pick them up because we can't combine
493 // split-stack and non-split-stack code in a single -r
494 // link, and libgo picks up non-split-stack code from
495 // libffi.
496 ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
497 ldflags = append(ldflags, goLibBegin...)
498
499 if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
500 ldflags = append(ldflags, nopie)
501 }
502
503 // We are creating an object file, so we don't want a build ID.
504 if root.buildID == "" {
505 ldflags = b.disableBuildID(ldflags)
506 }
507
508 realOut = out
509 out = out + ".o"
510
511 case "c-shared":
512 ldflags = append(ldflags, "-shared", "-nostdlib")
513 ldflags = append(ldflags, goLibBegin...)
514 ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
515
516 case "shared":
517 if cfg.Goos != "aix" {
518 ldflags = append(ldflags, "-zdefs")
519 }
520 ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
521
522 case "pie":
523 ldflags = append(ldflags, "-pie")
524
525 default:
526 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
527 }
528
529 switch buildmode {
530 case "exe", "c-shared":
531 if cxx {
532 ldflags = append(ldflags, "-lstdc++")
533 }
534 if objc {
535 ldflags = append(ldflags, "-lobjc")
536 }
537 if fortran {
538 fc := cfg.Getenv("FC")
539 if fc == "" {
540 fc = "gfortran"
541 }
542 // support gfortran out of the box and let others pass the correct link options
543 // via CGO_LDFLAGS
544 if strings.Contains(fc, "gfortran") {
545 ldflags = append(ldflags, "-lgfortran")
546 }
547 }
548 }
549
550 if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
551 return err
552 }
553
554 switch buildmode {
555 case "c-archive":
556 if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
557 return err
558 }
559 }
560 return nil
561 }
562
563 func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
564 return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
565 }
566
567 func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
568 return tools.link(b, root, out, importcfg, allactions, "shared", out)
569 }
570
571 func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
572 p := a.Package
573 inc := filepath.Join(cfg.GOROOT, "pkg", "include")
574 cfile = mkAbs(p.Dir, cfile)
575 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
576 defs = append(defs, b.gccArchArgs()...)
577 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
578 defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
579 }
580 compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
581 if b.gccSupportsFlag(compiler, "-fsplit-stack") {
582 defs = append(defs, "-fsplit-stack")
583 }
584 defs = tools.maybePIC(defs)
585 if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
586 defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.")
587 defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
588 } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
589 defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
590 }
591 if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
592 defs = append(defs, "-gno-record-gcc-switches")
593 }
594 return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
595 "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
596 }
597
598 // maybePIC adds -fPIC to the list of arguments if needed.
599 func (tools gccgoToolchain) maybePIC(args []string) []string {
600 switch cfg.BuildBuildmode {
601 case "c-archive", "c-shared", "shared", "plugin":
602 args = append(args, "-fPIC")
603 }
604 return args
605 }
606
607 func gccgoPkgpath(p *load.Package) string {
608 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
609 return ""
610 }
611 if p.ImportPath == "main" && p.Internal.ForceLibrary {
612 return "testmain"
613 }
614 return p.ImportPath
615 }
616
617 var gccgoToSymbolFuncOnce sync.Once
618 var gccgoToSymbolFunc func(string) string
619
620 func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string {
621 gccgoToSymbolFuncOnce.Do(func() {
622 if cfg.BuildN {
623 gccgoToSymbolFunc = func(s string) string { return s }
624 return
625 }
626 fn, err := pkgpath.ToSymbolFunc(tools.compiler(), b.WorkDir)
627 if err != nil {
628 fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
629 base.SetExitStatus(2)
630 base.Exit()
631 }
632 gccgoToSymbolFunc = fn
633 })
634
635 return gccgoToSymbolFunc(gccgoPkgpath(p))
636 }