From faafcc606e9b2e19bd8d9829aa55603cb291b1a1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Cl=C3=A9ment=20Chigot?= Date: Mon, 29 Oct 2018 13:39:53 +0100 Subject: [PATCH] cmd: allow build with gccgo on AIX This commit adapts cmd/internal/buildid and cmd/go to allow the use of gccgo on AIX. Buildid is supported only for AIX archives. Change-Id: I14c790a8994ae8d2ee629d8751e04189c30ffd94 Reviewed-on: https://go-review.googlesource.com/c/145417 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/cmd/go/internal/work/buildid.go | 13 +++-- src/cmd/go/internal/work/exec.go | 8 ++- src/cmd/go/internal/work/gccgo.go | 38 ++++++++++--- src/cmd/internal/buildid/buildid.go | 83 +++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 14 deletions(-) diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go index af3183ae9a..a6cfb50558 100644 --- a/src/cmd/go/internal/work/buildid.go +++ b/src/cmd/go/internal/work/buildid.go @@ -322,13 +322,16 @@ func assemblerIsGas() bool { } } -// gccgoBuildIDELFFile creates an assembler file that records the -// action's build ID in an SHF_EXCLUDE section. -func (b *Builder) gccgoBuildIDELFFile(a *Action) (string, error) { +// gccgoBuildIDFile creates an assembler file that records the +// action's build ID in an SHF_EXCLUDE section for ELF files or +// in a CSECT in XCOFF files. +func (b *Builder) gccgoBuildIDFile(a *Action) (string, error) { sfile := a.Objdir + "_buildid.s" var buf bytes.Buffer - if cfg.Goos != "solaris" || assemblerIsGas() { + if cfg.Goos == "aix" { + fmt.Fprintf(&buf, "\t.csect .go.buildid[XO]\n") + } else if cfg.Goos != "solaris" || assemblerIsGas() { fmt.Fprintf(&buf, "\t"+`.section .go.buildid,"e"`+"\n") } else if cfg.Goarch == "sparc" || cfg.Goarch == "sparc64" { fmt.Fprintf(&buf, "\t"+`.section ".go.buildid",#exclude`+"\n") @@ -347,7 +350,7 @@ func (b *Builder) gccgoBuildIDELFFile(a *Action) (string, error) { fmt.Fprintf(&buf, "%#02x", a.buildID[i]) } fmt.Fprintf(&buf, "\n") - if cfg.Goos != "solaris" { + if cfg.Goos != "solaris" && cfg.Goos != "aix" { secType := "@progbits" if cfg.Goarch == "arm" { secType = "%progbits" diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 99a500f11f..d9c59aab80 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -699,8 +699,8 @@ func (b *Builder) build(a *Action) (err error) { // This is read by readGccgoArchive in cmd/internal/buildid/buildid.go. if a.buildID != "" && cfg.BuildToolchainName == "gccgo" { switch cfg.Goos { - case "android", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": - asmfile, err := b.gccgoBuildIDELFFile(a) + case "aix", "android", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": + asmfile, err := b.gccgoBuildIDFile(a) if err != nil { return err } @@ -2297,6 +2297,10 @@ func (b *Builder) gccArchArgs() []string { return []string{"-mabi=64"} case "mips", "mipsle": return []string{"-mabi=32", "-march=mips32"} + case "ppc64": + if cfg.Goos == "aix" { + return []string{"-maix64"} + } } return nil } diff --git a/src/cmd/go/internal/work/gccgo.go b/src/cmd/go/internal/work/gccgo.go index 91daf529d4..ca3be4fd36 100644 --- a/src/cmd/go/internal/work/gccgo.go +++ b/src/cmd/go/internal/work/gccgo.go @@ -186,7 +186,15 @@ func (gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) for _, f := range ofiles { absOfiles = append(absOfiles, mkAbs(objdir, f)) } - return b.run(a, p.Dir, p.ImportPath, nil, "ar", "rc", mkAbs(objdir, afile), absOfiles) + 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. + // AIX "ar" command does not know D option. + arArgs = "-X64" + } + + return b.run(a, p.Dir, p.ImportPath, nil, "ar", arArgs, "rc", mkAbs(objdir, afile), absOfiles) } func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error { @@ -342,17 +350,24 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string } } - ldflags = append(ldflags, "-Wl,--whole-archive") + 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, "-Wl,--no-whole-archive") + ldflags = append(ldflags, noWholeArchive...) ldflags = append(ldflags, cgoldflags...) ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) if root.Package != nil { ldflags = append(ldflags, root.Package.CgoLDFLAGS...) } - - ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)") + if cfg.Goos != "aix" { + ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)") + } if root.buildID != "" { // On systems that normally use gold or the GNU linker, @@ -363,11 +378,17 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string } } + 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), - "-Wl,-rpath="+filepath.Dir(shlib), + rLibPath+filepath.Dir(shlib), "-l"+strings.TrimSuffix( strings.TrimPrefix(filepath.Base(shlib), "lib"), ".so")) @@ -412,7 +433,10 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string case "c-shared": ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc") case "shared": - ldflags = append(ldflags, "-zdefs", "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") + 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) diff --git a/src/cmd/internal/buildid/buildid.go b/src/cmd/internal/buildid/buildid.go index fa3d7f37ec..8205f696eb 100644 --- a/src/cmd/internal/buildid/buildid.go +++ b/src/cmd/internal/buildid/buildid.go @@ -6,6 +6,7 @@ package buildid import ( "bytes" + "cmd/internal/xcoff" "debug/elf" "fmt" "io" @@ -40,6 +41,9 @@ func ReadFile(name string) (id string, err error) { return "", err } if string(buf) != "!\n" { + if string(buf) == "\n" { + return readGccgoBigArchive(name, f) + } return readBinary(name, f) } @@ -157,6 +161,85 @@ func readGccgoArchive(name string, f *os.File) (string, error) { } } +// readGccgoBigArchive tries to parse the archive as an AIX big +// archive file, and fetch the build ID from the _buildid.o entry. +// The _buildid.o entry is written by (*Builder).gccgoBuildIDXCOFFFile +// in cmd/go/internal/work/exec.go. +func readGccgoBigArchive(name string, f *os.File) (string, error) { + bad := func() (string, error) { + return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed} + } + + // Read fixed-length header. + if _, err := f.Seek(0, io.SeekStart); err != nil { + return "", err + } + var flhdr [128]byte + if _, err := io.ReadFull(f, flhdr[:]); err != nil { + return "", err + } + // Read first member offset. + offStr := strings.TrimSpace(string(flhdr[68:88])) + off, err := strconv.ParseInt(offStr, 10, 64) + if err != nil { + return bad() + } + for { + if off == 0 { + // No more entries, no build ID. + return "", nil + } + if _, err := f.Seek(off, io.SeekStart); err != nil { + return "", err + } + // Read member header. + var hdr [112]byte + if _, err := io.ReadFull(f, hdr[:]); err != nil { + return "", err + } + // Read member name length. + namLenStr := strings.TrimSpace(string(hdr[108:112])) + namLen, err := strconv.ParseInt(namLenStr, 10, 32) + if err != nil { + return bad() + } + if namLen == 10 { + var nam [10]byte + if _, err := io.ReadFull(f, nam[:]); err != nil { + return "", err + } + if string(nam[:]) == "_buildid.o" { + sizeStr := strings.TrimSpace(string(hdr[0:20])) + size, err := strconv.ParseInt(sizeStr, 10, 64) + if err != nil { + return bad() + } + off += int64(len(hdr)) + namLen + 2 + if off&1 != 0 { + off++ + } + sr := io.NewSectionReader(f, off, size) + x, err := xcoff.NewFile(sr) + if err != nil { + return bad() + } + data := x.CSect(".go.buildid") + if data == nil { + return bad() + } + return string(data), nil + } + } + + // Read next member offset. + offStr = strings.TrimSpace(string(hdr[20:40])) + off, err = strconv.ParseInt(offStr, 10, 64) + if err != nil { + return bad() + } + } +} + var ( goBuildPrefix = []byte("\xff Go build ID: \"") goBuildEnd = []byte("\"\n \xff") -- 2.50.0