This patch by Tony Reix adds buildid support to the go tool for AIX. This supports caching of build results. Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu. Committed to mainline.
Ian
Index: gcc/go/gofrontend/MERGE =================================================================== --- gcc/go/gofrontend/MERGE (revision 256877) +++ gcc/go/gofrontend/MERGE (working copy) @@ -1,4 +1,4 @@ -87525458bcd5ab4beb5b95e7d58e3dfdbc1bd478 +3488a401e50835de5de5c4f153772ac2798d0e71 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. Index: libgo/go/cmd/go/internal/work/buildid.go =================================================================== --- libgo/go/cmd/go/internal/work/buildid.go (revision 256794) +++ libgo/go/cmd/go/internal/work/buildid.go (working copy) @@ -330,6 +330,43 @@ func (b *Builder) gccgoBuildIDELFFile(a return sfile, nil } +// gccgoBuildIDXCOFFFile creates an assembler file that records the +// action's build ID in a CSECT (AIX linker deletes CSECTs that are +// not referenced in the output file). +func (b *Builder) gccgoBuildIDXCOFFFile(a *Action) (string, error) { + sfile := a.Objdir + "_buildid.s" + + var buf bytes.Buffer + fmt.Fprintf(&buf, "\t.csect .go.buildid[XO]\n") + fmt.Fprintf(&buf, "\t.byte ") + for i := 0; i < len(a.buildID); i++ { + if i > 0 { + if i%8 == 0 { + fmt.Fprintf(&buf, "\n\t.byte ") + } else { + fmt.Fprintf(&buf, ",") + } + } + fmt.Fprintf(&buf, "%#02x", a.buildID[i]) + } + fmt.Fprintf(&buf, "\n") + + if cfg.BuildN || cfg.BuildX { + for _, line := range bytes.Split(buf.Bytes(), []byte("\n")) { + b.Showcmd("", "echo '%s' >> %s", line, sfile) + } + if cfg.BuildN { + return sfile, nil + } + } + + if err := ioutil.WriteFile(sfile, buf.Bytes(), 0666); err != nil { + return "", err + } + + return sfile, nil +} + // buildID returns the build ID found in the given file. // If no build ID is found, buildID returns the content hash of the file. func (b *Builder) buildID(file string) string { Index: libgo/go/cmd/go/internal/work/exec.go =================================================================== --- libgo/go/cmd/go/internal/work/exec.go (revision 256873) +++ libgo/go/cmd/go/internal/work/exec.go (working copy) @@ -637,6 +637,16 @@ func (b *Builder) build(a *Action) (err return err } objects = append(objects, ofiles...) + case "aix": + asmfile, err := b.gccgoBuildIDXCOFFFile(a) + if err != nil { + return err + } + ofiles, err := BuildToolchain.asm(b, a, []string{asmfile}) + if err != nil { + return err + } + objects = append(objects, ofiles...) } } Index: libgo/go/cmd/internal/buildid/buildid.go =================================================================== --- libgo/go/cmd/internal/buildid/buildid.go (revision 256593) +++ libgo/go/cmd/internal/buildid/buildid.go (working copy) @@ -7,6 +7,7 @@ package buildid import ( "bytes" "debug/elf" + "debug/xcoff" "fmt" "io" "os" @@ -40,6 +41,9 @@ func ReadFile(name string) (id string, e return "", err } if string(buf) != "!<arch>\n" { + if string(buf) == "<bigaf>\n" { + return readGccgoBigArchive(name, f) + } return readBinary(name, f) } @@ -156,6 +160,85 @@ func readGccgoArchive(name string, f *os } } } + +// 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: \"")