libgo patch committed: Add buildid support for AIX

Message ID CAOyqgcUvd8wicCKf=nTKjij-+ELFBnb75xpwwWd21MVSo-ymdw@mail.gmail.com
State New
Headers show
Series
  • libgo patch committed: Add buildid support for AIX
Related show

Commit Message

Ian Lance Taylor Jan. 23, 2018, 4:44 a.m.
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

Patch

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: \"")