summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/work/gccgo.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/go/internal/work/gccgo.go
parentInitial commit. (diff)
downloadgolang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.tar.xz
golang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.zip
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/go/internal/work/gccgo.go')
-rw-r--r--src/cmd/go/internal/work/gccgo.go618
1 files changed, 618 insertions, 0 deletions
diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go
new file mode 100644
index 0000000..b58c8aa
--- /dev/null
+++ b/src/cmd/go/internal/work/gccgo.go
@@ -0,0 +1,618 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package work
+
+import (
+ "fmt"
+ exec "internal/execabs"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+
+ "cmd/go/internal/base"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
+ "cmd/go/internal/load"
+ "cmd/go/internal/str"
+ "cmd/internal/pkgpath"
+)
+
+// The Gccgo toolchain.
+
+type gccgoToolchain struct{}
+
+var GccgoName, GccgoBin string
+var gccgoErr error
+
+func init() {
+ GccgoName = cfg.Getenv("GCCGO")
+ if GccgoName == "" {
+ GccgoName = "gccgo"
+ }
+ GccgoBin, gccgoErr = exec.LookPath(GccgoName)
+}
+
+func (gccgoToolchain) compiler() string {
+ checkGccgoBin()
+ return GccgoBin
+}
+
+func (gccgoToolchain) linker() string {
+ checkGccgoBin()
+ return GccgoBin
+}
+
+func (gccgoToolchain) ar() string {
+ ar := cfg.Getenv("AR")
+ if ar == "" {
+ ar = "ar"
+ }
+ return ar
+}
+
+func checkGccgoBin() {
+ if gccgoErr == nil {
+ return
+ }
+ fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
+ base.SetExitStatus(2)
+ base.Exit()
+}
+
+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) {
+ p := a.Package
+ objdir := a.Objdir
+ out := "_go_.o"
+ ofile = objdir + out
+ gcargs := []string{"-g"}
+ gcargs = append(gcargs, b.gccArchArgs()...)
+ gcargs = append(gcargs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
+ gcargs = append(gcargs, "-gno-record-gcc-switches")
+ if pkgpath := gccgoPkgpath(p); pkgpath != "" {
+ gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
+ }
+ if p.Internal.LocalPrefix != "" {
+ gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
+ }
+
+ args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
+ if importcfg != nil {
+ if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
+ if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
+ return "", nil, err
+ }
+ args = append(args, "-fgo-importcfg="+objdir+"importcfg")
+ } else {
+ root := objdir + "_importcfgroot_"
+ if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
+ return "", nil, err
+ }
+ args = append(args, "-I", root)
+ }
+ }
+ if embedcfg != nil && b.gccSupportsFlag(args[:1], "-fgo-embedcfg=/dev/null") {
+ if err := b.writeFile(objdir+"embedcfg", embedcfg); err != nil {
+ return "", nil, err
+ }
+ args = append(args, "-fgo-embedcfg="+objdir+"embedcfg")
+ }
+
+ if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
+ if cfg.BuildTrimpath {
+ args = append(args, "-ffile-prefix-map="+base.Cwd+"=.")
+ args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
+ }
+ if fsys.OverlayFile != "" {
+ for _, name := range gofiles {
+ absPath := mkAbs(p.Dir, name)
+ overlayPath, ok := fsys.OverlayPath(absPath)
+ if !ok {
+ continue
+ }
+ toPath := absPath
+ // gccgo only applies the last matching rule, so also handle the case where
+ // BuildTrimpath is true and the path is relative to base.Cwd.
+ if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd) {
+ toPath = "." + toPath[len(base.Cwd):]
+ }
+ args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath)
+ }
+ }
+ }
+
+ args = append(args, a.Package.Internal.Gccgoflags...)
+ for _, f := range gofiles {
+ f := mkAbs(p.Dir, f)
+ // Overlay files if necessary.
+ // See comment on gctoolchain.gc about overlay TODOs
+ f, _ = fsys.OverlayPath(f)
+ args = append(args, f)
+ }
+
+ output, err = b.runOut(a, p.Dir, nil, args)
+ return ofile, output, err
+}
+
+// buildImportcfgSymlinks builds in root a tree of symlinks
+// implementing the directives from importcfg.
+// This serves as a temporary transition mechanism until
+// we can depend on gccgo reading an importcfg directly.
+// (The Go 1.9 and later gc compilers already do.)
+func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
+ for lineNum, line := range strings.Split(string(importcfg), "\n") {
+ lineNum++ // 1-based
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ var verb, args string
+ if i := strings.Index(line, " "); i < 0 {
+ verb = line
+ } else {
+ verb, args = line[:i], strings.TrimSpace(line[i+1:])
+ }
+ var before, after string
+ if i := strings.Index(args, "="); i >= 0 {
+ before, after = args[:i], args[i+1:]
+ }
+ switch verb {
+ default:
+ base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
+ case "packagefile":
+ if before == "" || after == "" {
+ return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
+ }
+ archive := gccgoArchive(root, before)
+ if err := b.Mkdir(filepath.Dir(archive)); err != nil {
+ return err
+ }
+ if err := b.Symlink(after, archive); err != nil {
+ return err
+ }
+ case "importmap":
+ if before == "" || after == "" {
+ return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
+ }
+ beforeA := gccgoArchive(root, before)
+ afterA := gccgoArchive(root, after)
+ if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
+ return err
+ }
+ if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
+ return err
+ }
+ if err := b.Symlink(afterA, beforeA); err != nil {
+ return err
+ }
+ case "packageshlib":
+ return fmt.Errorf("gccgo -importcfg does not support shared libraries")
+ }
+ }
+ return nil
+}
+
+func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
+ p := a.Package
+ var ofiles []string
+ for _, sfile := range sfiles {
+ base := filepath.Base(sfile)
+ ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
+ ofiles = append(ofiles, ofile)
+ sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile))
+ defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
+ if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
+ defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
+ }
+ defs = tools.maybePIC(defs)
+ defs = append(defs, b.gccArchArgs()...)
+ err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return ofiles, nil
+}
+
+func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
+ return "", nil
+}
+
+func gccgoArchive(basedir, imp string) string {
+ end := filepath.FromSlash(imp + ".a")
+ afile := filepath.Join(basedir, end)
+ // add "lib" to the final element
+ return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
+}
+
+func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
+ p := a.Package
+ objdir := a.Objdir
+ var absOfiles []string
+ for _, f := range ofiles {
+ absOfiles = append(absOfiles, mkAbs(objdir, f))
+ }
+ var arArgs []string
+ if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
+ // AIX puts both 32-bit and 64-bit objects in the same archive.
+ // Tell the AIX "ar" command to only care about 64-bit objects.
+ arArgs = []string{"-X64"}
+ }
+ absAfile := mkAbs(objdir, afile)
+ // Try with D modifier first, then without if that fails.
+ output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
+ if err != nil {
+ return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
+ }
+
+ if len(output) > 0 {
+ // Show the output if there is any even without errors.
+ b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output))
+ }
+
+ return nil
+}
+
+func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
+ // gccgo needs explicit linking with all package dependencies,
+ // and all LDFLAGS from cgo dependencies.
+ afiles := []string{}
+ shlibs := []string{}
+ ldflags := b.gccArchArgs()
+ cgoldflags := []string{}
+ usesCgo := false
+ cxx := false
+ objc := false
+ fortran := false
+ if root.Package != nil {
+ cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
+ objc = len(root.Package.MFiles) > 0
+ fortran = len(root.Package.FFiles) > 0
+ }
+
+ readCgoFlags := func(flagsFile string) error {
+ flags, err := os.ReadFile(flagsFile)
+ if err != nil {
+ return err
+ }
+ const ldflagsPrefix = "_CGO_LDFLAGS="
+ for _, line := range strings.Split(string(flags), "\n") {
+ if strings.HasPrefix(line, ldflagsPrefix) {
+ newFlags := strings.Fields(line[len(ldflagsPrefix):])
+ for _, flag := range newFlags {
+ // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
+ // but they don't mean anything to the linker so filter
+ // them out.
+ if flag != "-g" && !strings.HasPrefix(flag, "-O") {
+ cgoldflags = append(cgoldflags, flag)
+ }
+ }
+ }
+ }
+ return nil
+ }
+
+ var arArgs []string
+ if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
+ // AIX puts both 32-bit and 64-bit objects in the same archive.
+ // Tell the AIX "ar" command to only care about 64-bit objects.
+ arArgs = []string{"-X64"}
+ }
+
+ newID := 0
+ readAndRemoveCgoFlags := func(archive string) (string, error) {
+ newID++
+ newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
+ if err := b.copyFile(newArchive, archive, 0666, false); err != nil {
+ return "", err
+ }
+ if cfg.BuildN || cfg.BuildX {
+ b.Showcmd("", "ar d %s _cgo_flags", newArchive)
+ if cfg.BuildN {
+ // TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
+ // Either the archive is already built and we can read them out,
+ // or we're printing commands to build the archive and can
+ // forward the _cgo_flags directly to this step.
+ return "", nil
+ }
+ }
+ err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
+ if err != nil {
+ return "", err
+ }
+ err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
+ if err != nil {
+ return "", err
+ }
+ err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
+ if err != nil {
+ return "", err
+ }
+ return newArchive, nil
+ }
+
+ // If using -linkshared, find the shared library deps.
+ haveShlib := make(map[string]bool)
+ targetBase := filepath.Base(root.Target)
+ if cfg.BuildLinkshared {
+ for _, a := range root.Deps {
+ p := a.Package
+ if p == nil || p.Shlib == "" {
+ continue
+ }
+
+ // The .a we are linking into this .so
+ // will have its Shlib set to this .so.
+ // Don't start thinking we want to link
+ // this .so into itself.
+ base := filepath.Base(p.Shlib)
+ if base != targetBase {
+ haveShlib[base] = true
+ }
+ }
+ }
+
+ // Arrange the deps into afiles and shlibs.
+ addedShlib := make(map[string]bool)
+ for _, a := range root.Deps {
+ p := a.Package
+ if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] {
+ // This is a package linked into a shared
+ // library that we will put into shlibs.
+ continue
+ }
+
+ if haveShlib[filepath.Base(a.Target)] {
+ // This is a shared library we want to link against.
+ if !addedShlib[a.Target] {
+ shlibs = append(shlibs, a.Target)
+ addedShlib[a.Target] = true
+ }
+ continue
+ }
+
+ if p != nil {
+ target := a.built
+ if p.UsesCgo() || p.UsesSwig() {
+ var err error
+ target, err = readAndRemoveCgoFlags(target)
+ if err != nil {
+ continue
+ }
+ }
+
+ afiles = append(afiles, target)
+ }
+ }
+
+ for _, a := range allactions {
+ // Gather CgoLDFLAGS, but not from standard packages.
+ // The go tool can dig up runtime/cgo from GOROOT and
+ // think that it should use its CgoLDFLAGS, but gccgo
+ // doesn't use runtime/cgo.
+ if a.Package == nil {
+ continue
+ }
+ if !a.Package.Standard {
+ cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...)
+ }
+ if len(a.Package.CgoFiles) > 0 {
+ usesCgo = true
+ }
+ if a.Package.UsesSwig() {
+ usesCgo = true
+ }
+ if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
+ cxx = true
+ }
+ if len(a.Package.MFiles) > 0 {
+ objc = true
+ }
+ if len(a.Package.FFiles) > 0 {
+ fortran = true
+ }
+ }
+
+ wholeArchive := []string{"-Wl,--whole-archive"}
+ noWholeArchive := []string{"-Wl,--no-whole-archive"}
+ if cfg.Goos == "aix" {
+ wholeArchive = nil
+ noWholeArchive = nil
+ }
+ ldflags = append(ldflags, wholeArchive...)
+ ldflags = append(ldflags, afiles...)
+ ldflags = append(ldflags, noWholeArchive...)
+
+ ldflags = append(ldflags, cgoldflags...)
+ ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
+ if root.Package != nil {
+ ldflags = append(ldflags, root.Package.CgoLDFLAGS...)
+ }
+ if cfg.Goos != "aix" {
+ ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
+ }
+
+ if root.buildID != "" {
+ // On systems that normally use gold or the GNU linker,
+ // use the --build-id option to write a GNU build ID note.
+ switch cfg.Goos {
+ case "android", "dragonfly", "linux", "netbsd":
+ ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
+ }
+ }
+
+ var rLibPath string
+ if cfg.Goos == "aix" {
+ rLibPath = "-Wl,-blibpath="
+ } else {
+ rLibPath = "-Wl,-rpath="
+ }
+ for _, shlib := range shlibs {
+ ldflags = append(
+ ldflags,
+ "-L"+filepath.Dir(shlib),
+ rLibPath+filepath.Dir(shlib),
+ "-l"+strings.TrimSuffix(
+ strings.TrimPrefix(filepath.Base(shlib), "lib"),
+ ".so"))
+ }
+
+ var realOut string
+ goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
+ switch buildmode {
+ case "exe":
+ if usesCgo && cfg.Goos == "linux" {
+ ldflags = append(ldflags, "-Wl,-E")
+ }
+
+ case "c-archive":
+ // Link the Go files into a single .o, and also link
+ // in -lgolibbegin.
+ //
+ // We need to use --whole-archive with -lgolibbegin
+ // because it doesn't define any symbols that will
+ // cause the contents to be pulled in; it's just
+ // initialization code.
+ //
+ // The user remains responsible for linking against
+ // -lgo -lpthread -lm in the final link. We can't use
+ // -r to pick them up because we can't combine
+ // split-stack and non-split-stack code in a single -r
+ // link, and libgo picks up non-split-stack code from
+ // libffi.
+ ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
+ ldflags = append(ldflags, goLibBegin...)
+
+ if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
+ ldflags = append(ldflags, nopie)
+ }
+
+ // We are creating an object file, so we don't want a build ID.
+ if root.buildID == "" {
+ ldflags = b.disableBuildID(ldflags)
+ }
+
+ realOut = out
+ out = out + ".o"
+
+ case "c-shared":
+ ldflags = append(ldflags, "-shared", "-nostdlib")
+ ldflags = append(ldflags, goLibBegin...)
+ ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
+
+ case "shared":
+ if cfg.Goos != "aix" {
+ ldflags = append(ldflags, "-zdefs")
+ }
+ ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
+
+ default:
+ base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
+ }
+
+ switch buildmode {
+ case "exe", "c-shared":
+ if cxx {
+ ldflags = append(ldflags, "-lstdc++")
+ }
+ if objc {
+ ldflags = append(ldflags, "-lobjc")
+ }
+ if fortran {
+ fc := cfg.Getenv("FC")
+ if fc == "" {
+ fc = "gfortran"
+ }
+ // support gfortran out of the box and let others pass the correct link options
+ // via CGO_LDFLAGS
+ if strings.Contains(fc, "gfortran") {
+ ldflags = append(ldflags, "-lgfortran")
+ }
+ }
+ }
+
+ if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
+ return err
+ }
+
+ switch buildmode {
+ case "c-archive":
+ if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
+ return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
+}
+
+func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
+ return tools.link(b, root, out, importcfg, allactions, "shared", out)
+}
+
+func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
+ p := a.Package
+ inc := filepath.Join(cfg.GOROOT, "pkg", "include")
+ cfile = mkAbs(p.Dir, cfile)
+ defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
+ defs = append(defs, b.gccArchArgs()...)
+ if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
+ defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
+ }
+ compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
+ if b.gccSupportsFlag(compiler, "-fsplit-stack") {
+ defs = append(defs, "-fsplit-stack")
+ }
+ defs = tools.maybePIC(defs)
+ if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
+ defs = append(defs, "-ffile-prefix-map="+base.Cwd+"=.")
+ defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
+ } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
+ defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
+ }
+ if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
+ defs = append(defs, "-gno-record-gcc-switches")
+ }
+ return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
+ "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
+}
+
+// maybePIC adds -fPIC to the list of arguments if needed.
+func (tools gccgoToolchain) maybePIC(args []string) []string {
+ switch cfg.BuildBuildmode {
+ case "c-shared", "shared", "plugin":
+ args = append(args, "-fPIC")
+ }
+ return args
+}
+
+func gccgoPkgpath(p *load.Package) string {
+ if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
+ return ""
+ }
+ return p.ImportPath
+}
+
+var gccgoToSymbolFuncOnce sync.Once
+var gccgoToSymbolFunc func(string) string
+
+func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string {
+ gccgoToSymbolFuncOnce.Do(func() {
+ fn, err := pkgpath.ToSymbolFunc(tools.compiler(), b.WorkDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
+ base.SetExitStatus(2)
+ base.Exit()
+ }
+ gccgoToSymbolFunc = fn
+ })
+
+ return gccgoToSymbolFunc(gccgoPkgpath(p))
+}