cmd/go: buildid support for AIX archives.

Reviewed-on: https://go-review.googlesource.com/88935

From-SVN: r256971
This commit is contained in:
Ian Lance Taylor 2018-01-23 04:44:12 +00:00
parent f991f1022c
commit 38ad6f8a44
4 changed files with 131 additions and 1 deletions

View 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.

View 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 {

View 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...)
}
}

View 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")