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 {
import (
"bytes"
"debug/elf"
+ "debug/xcoff"
"fmt"
"io"
"os"
return "", err
}
if string(buf) != "!<arch>\n" {
+ if string(buf) == "<bigaf>\n" {
+ return readGccgoBigArchive(name, f)
+ }
return readBinary(name, f)
}
}
}
+// 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")