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.
9 exec "internal/execabs"
15 "cmd/go/internal/base"
17 "cmd/go/internal/fsys"
18 "cmd/go/internal/load"
20 "cmd/internal/pkgpath"
23 // The Gccgo toolchain.
25 type gccgoToolchain struct{}
27 var GccgoName, GccgoBin string
31 GccgoName = cfg.Getenv("GCCGO")
33 GccgoName = cfg.DefaultGCCGO(cfg.Goos, cfg.Goarch)
35 GccgoBin, gccgoErr = exec.LookPath(GccgoName)
38 func (gccgoToolchain) compiler() string {
43 func (gccgoToolchain) linker() string {
48 func (gccgoToolchain) ar() string {
49 ar := cfg.Getenv("AR")
56 func checkGccgoBin() {
60 fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
65 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) {
70 gcargs := []string{"-g"}
71 gcargs = append(gcargs, b.gccArchArgs()...)
72 gcargs = append(gcargs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
73 gcargs = append(gcargs, "-gno-record-gcc-switches")
74 if pkgpath := gccgoPkgpath(p); pkgpath != "" {
75 gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
77 if p.Internal.LocalPrefix != "" {
78 gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
81 args := str.StringList(tools.compiler(), "-c", "-O2", gcargs, "-o", ofile, forcedGccgoflags)
83 if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
84 if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
87 args = append(args, "-fgo-importcfg="+objdir+"importcfg")
89 root := objdir + "_importcfgroot_"
90 if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
93 args = append(args, "-I", root)
96 if embedcfg != nil && b.gccSupportsFlag(args[:1], "-fgo-embedcfg=/dev/null") {
97 if err := b.writeFile(objdir+"embedcfg", embedcfg); err != nil {
100 args = append(args, "-fgo-embedcfg="+objdir+"embedcfg")
103 if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
104 if cfg.BuildTrimpath {
105 args = append(args, "-ffile-prefix-map="+base.Cwd()+"=.")
106 args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
108 if fsys.OverlayFile != "" {
109 for _, name := range gofiles {
110 absPath := mkAbs(p.Dir, name)
111 overlayPath, ok := fsys.OverlayPath(absPath)
116 // gccgo only applies the last matching rule, so also handle the case where
117 // BuildTrimpath is true and the path is relative to base.Cwd().
118 if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd()) {
119 toPath = "." + toPath[len(base.Cwd()):]
121 args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath)
126 args = append(args, a.Package.Internal.Gccgoflags...)
127 for _, f := range gofiles {
129 // Overlay files if necessary.
130 // See comment on gctoolchain.gc about overlay TODOs
131 f, _ = fsys.OverlayPath(f)
132 args = append(args, f)
135 output, err = b.runOut(a, p.Dir, nil, args)
136 return ofile, output, err
139 // buildImportcfgSymlinks builds in root a tree of symlinks
140 // implementing the directives from importcfg.
141 // This serves as a temporary transition mechanism until
142 // we can depend on gccgo reading an importcfg directly.
143 // (The Go 1.9 and later gc compilers already do.)
144 func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
145 for lineNum, line := range strings.Split(string(importcfg), "\n") {
147 line = strings.TrimSpace(line)
151 if line == "" || strings.HasPrefix(line, "#") {
154 var verb, args string
155 if i := strings.Index(line, " "); i < 0 {
158 verb, args = line[:i], strings.TrimSpace(line[i+1:])
160 var before, after string
161 if i := strings.Index(args, "="); i >= 0 {
162 before, after = args[:i], args[i+1:]
166 base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
168 if before == "" || after == "" {
169 return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
171 archive := gccgoArchive(root, before)
172 if err := b.Mkdir(filepath.Dir(archive)); err != nil {
175 if err := b.Symlink(after, archive); err != nil {
179 if before == "" || after == "" {
180 return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
182 beforeA := gccgoArchive(root, before)
183 afterA := gccgoArchive(root, after)
184 if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
187 if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
190 if err := b.Symlink(afterA, beforeA); err != nil {
194 return fmt.Errorf("gccgo -importcfg does not support shared libraries")
200 func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
203 for _, sfile := range sfiles {
204 base := filepath.Base(sfile)
205 ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
206 ofiles = append(ofiles, ofile)
207 sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile))
208 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
209 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
210 defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
212 defs = tools.maybePIC(defs)
213 defs = append(defs, b.gccArchArgs()...)
214 err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
222 func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
226 func gccgoArchive(basedir, imp string) string {
227 end := filepath.FromSlash(imp + ".a")
228 afile := filepath.Join(basedir, end)
229 // add "lib" to the final element
230 return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
233 func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
236 var absOfiles []string
237 for _, f := range ofiles {
238 absOfiles = append(absOfiles, mkAbs(objdir, f))
241 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
242 // AIX puts both 32-bit and 64-bit objects in the same archive.
243 // Tell the AIX "ar" command to only care about 64-bit objects.
244 arArgs = []string{"-X64"}
246 absAfile := mkAbs(objdir, afile)
247 // Try with D modifier first, then without if that fails.
248 output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
250 return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
254 // Show the output if there is any even without errors.
255 b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output))
261 func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
262 // gccgo needs explicit linking with all package dependencies,
263 // and all LDFLAGS from cgo dependencies.
266 ldflags := b.gccArchArgs()
267 cgoldflags := []string{}
272 if root.Package != nil {
273 cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
274 objc = len(root.Package.MFiles) > 0
275 fortran = len(root.Package.FFiles) > 0
278 readCgoFlags := func(flagsFile string) error {
279 flags, err := os.ReadFile(flagsFile)
283 const ldflagsPrefix = "_CGO_LDFLAGS="
284 for _, line := range strings.Split(string(flags), "\n") {
285 if strings.HasPrefix(line, ldflagsPrefix) {
286 line = line[len(ldflagsPrefix):]
293 if quote == 0 && (b == ' ' || b == '\t') {
295 cgoldflags = append(cgoldflags, string(nl))
300 } else if b == '"' || b == '\'' {
302 } else if b == quote {
304 } else if quote == 0 && start && b == '-' && strings.HasPrefix(line, "g") {
307 } else if quote == 0 && start && b == '-' && strings.HasPrefix(line, "O") {
308 for len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
317 cgoldflags = append(cgoldflags, string(nl))
325 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
326 // AIX puts both 32-bit and 64-bit objects in the same archive.
327 // Tell the AIX "ar" command to only care about 64-bit objects.
328 arArgs = []string{"-X64"}
332 readAndRemoveCgoFlags := func(archive string) (string, error) {
334 newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
335 if err := b.copyFile(newArchive, archive, 0666, false); err != nil {
339 // TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
340 // Either the archive is already built and we can read them out,
341 // or we're printing commands to build the archive and can
342 // forward the _cgo_flags directly to this step.
343 b.Showcmd("", "ar d %s _cgo_flags", newArchive)
346 err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
350 err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
354 err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
358 return newArchive, nil
361 // If using -linkshared, find the shared library deps.
362 haveShlib := make(map[string]bool)
363 targetBase := filepath.Base(root.Target)
364 if cfg.BuildLinkshared {
365 for _, a := range root.Deps {
367 if p == nil || p.Shlib == "" {
371 // The .a we are linking into this .so
372 // will have its Shlib set to this .so.
373 // Don't start thinking we want to link
374 // this .so into itself.
375 base := filepath.Base(p.Shlib)
376 if base != targetBase {
377 haveShlib[base] = true
382 // Arrange the deps into afiles and shlibs.
383 addedShlib := make(map[string]bool)
384 for _, a := range root.Deps {
386 if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] {
387 // This is a package linked into a shared
388 // library that we will put into shlibs.
392 if haveShlib[filepath.Base(a.Target)] {
393 // This is a shared library we want to link against.
394 if !addedShlib[a.Target] {
395 shlibs = append(shlibs, a.Target)
396 addedShlib[a.Target] = true
403 if p.UsesCgo() || p.UsesSwig() {
405 target, err = readAndRemoveCgoFlags(target)
411 afiles = append(afiles, target)
415 for _, a := range allactions {
416 if a.Package == nil {
419 if len(a.Package.CgoFiles) > 0 {
422 if a.Package.UsesSwig() {
425 if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
428 if len(a.Package.MFiles) > 0 {
431 if len(a.Package.FFiles) > 0 {
436 wholeArchive := []string{"-Wl,--whole-archive"}
437 noWholeArchive := []string{"-Wl,--no-whole-archive"}
438 if cfg.Goos == "aix" {
442 ldflags = append(ldflags, wholeArchive...)
443 ldflags = append(ldflags, afiles...)
444 ldflags = append(ldflags, noWholeArchive...)
446 ldflags = append(ldflags, cgoldflags...)
447 ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
448 if cfg.Goos != "aix" {
449 ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
452 if root.buildID != "" {
453 // On systems that normally use gold or the GNU linker,
454 // use the --build-id option to write a GNU build ID note.
456 case "android", "dragonfly", "linux", "netbsd":
457 ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
462 if cfg.Goos == "aix" {
463 rLibPath = "-Wl,-blibpath="
465 rLibPath = "-Wl,-rpath="
467 for _, shlib := range shlibs {
470 "-L"+filepath.Dir(shlib),
471 rLibPath+filepath.Dir(shlib),
472 "-l"+strings.TrimSuffix(
473 strings.TrimPrefix(filepath.Base(shlib), "lib"),
478 goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
481 if usesCgo && cfg.Goos == "linux" {
482 ldflags = append(ldflags, "-Wl,-E")
486 // Link the Go files into a single .o, and also link
489 // We need to use --whole-archive with -lgolibbegin
490 // because it doesn't define any symbols that will
491 // cause the contents to be pulled in; it's just
492 // initialization code.
494 // The user remains responsible for linking against
495 // -lgo -lpthread -lm in the final link. We can't use
496 // -r to pick them up because we can't combine
497 // split-stack and non-split-stack code in a single -r
498 // link, and libgo picks up non-split-stack code from
500 ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
501 ldflags = append(ldflags, goLibBegin...)
503 if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
504 ldflags = append(ldflags, nopie)
507 // We are creating an object file, so we don't want a build ID.
508 if root.buildID == "" {
509 ldflags = b.disableBuildID(ldflags)
516 ldflags = append(ldflags, "-shared", "-nostdlib")
517 ldflags = append(ldflags, goLibBegin...)
518 ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
521 if cfg.Goos != "aix" {
522 ldflags = append(ldflags, "-zdefs")
524 ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
527 ldflags = append(ldflags, "-pie")
530 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
534 case "exe", "c-shared":
536 ldflags = append(ldflags, "-lstdc++")
539 ldflags = append(ldflags, "-lobjc")
542 fc := cfg.Getenv("FC")
546 // support gfortran out of the box and let others pass the correct link options
548 if strings.Contains(fc, "gfortran") {
549 ldflags = append(ldflags, "-lgfortran")
554 if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
560 if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
567 func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
568 return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
571 func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
572 return tools.link(b, root, out, importcfg, allactions, "shared", out)
575 func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
577 inc := filepath.Join(cfg.GOROOT, "pkg", "include")
578 cfile = mkAbs(p.Dir, cfile)
579 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
580 defs = append(defs, b.gccArchArgs()...)
581 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
582 defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
584 compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
585 if b.gccSupportsFlag(compiler, "-fsplit-stack") {
586 defs = append(defs, "-fsplit-stack")
588 defs = tools.maybePIC(defs)
589 if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
590 defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.")
591 defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
592 } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
593 defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
595 if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
596 defs = append(defs, "-gno-record-gcc-switches")
598 return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
599 "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
602 // maybePIC adds -fPIC to the list of arguments if needed.
603 func (tools gccgoToolchain) maybePIC(args []string) []string {
604 switch cfg.BuildBuildmode {
605 case "c-archive", "c-shared", "shared", "plugin":
606 args = append(args, "-fPIC")
611 func gccgoPkgpath(p *load.Package) string {
612 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
615 if p.ImportPath == "main" && p.Internal.ForceLibrary {
621 var gccgoToSymbolFuncOnce sync.Once
622 var gccgoToSymbolFunc func(string) string
624 func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string {
625 gccgoToSymbolFuncOnce.Do(func() {
627 gccgoToSymbolFunc = func(s string) string { return s }
630 fn, err := pkgpath.ToSymbolFunc(tools.compiler(), b.WorkDir)
632 fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
633 base.SetExitStatus(2)
636 gccgoToSymbolFunc = fn
639 return gccgoToSymbolFunc(gccgoPkgpath(p))