cmd/go: buildid support for AIX archives.
authorIan Lance Taylor <ian@gcc.gnu.org>
Tue, 23 Jan 2018 04:44:12 +0000 (04:44 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 23 Jan 2018 04:44:12 +0000 (04:44 +0000)
    Reviewed-on: https://go-review.googlesource.com/88935

From-SVN: r256971

gcc/go/gofrontend/MERGE
libgo/go/cmd/go/internal/work/buildid.go
libgo/go/cmd/go/internal/work/exec.go
libgo/go/cmd/internal/buildid/buildid.go

index a66fe24dfe2876c122fb7899cd6bec4f4f98f8ab..a100cab84fe5e3f8d4029a2e235a1cbf5e5e3f3c 100644 (file)
@@ -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 39ca20ee4f0c9cc276b852e7dc66d93b278f36ae..e2ae85083d8bc628e1ae64b3a03e72517cba9674 100644 (file)
@@ -330,6 +330,43 @@ func (b *Builder) gccgoBuildIDELFFile(a *Action) (string, error) {
        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 c2704c4335ecf38df1315dad3118580906ce5c94..edd2694b086dfc6914a737900450e4d702a42115 100644 (file)
@@ -637,6 +637,16 @@ func (b *Builder) build(a *Action) (err error) {
                                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 fa3d7f37ec60d8b51423e862d9ea46fd145029f8..41e6c773e1b6eec049d9b12270dd68d94ab39a9f 100644 (file)
@@ -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, err error) {
                return "", err
        }
        if string(buf) != "!<arch>\n" {
+               if string(buf) == "<bigaf>\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")