mirror of
https://github.com/golang/go
synced 2024-11-06 13:46:16 -07:00
go/internal/gccgoimporter: read export data from archives
When used with the go tool, gccgo will normally generate archive files. This change teaches the gccgoimporter package how to read the export data from an archive. This is needed by, for example, cmd/vet, when typechecking packages. Change-Id: I21267949a7808cd81c0042af425c774a4ff7d82f Reviewed-on: https://go-review.googlesource.com/119895 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
29673a4be6
commit
0c9be48a90
148
src/go/internal/gccgoimporter/ar.go
Normal file
148
src/go/internal/gccgoimporter/ar.go
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gccgoimporter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Magic strings for different archive file formats.
|
||||
const (
|
||||
armag = "!<arch>\n"
|
||||
armagt = "!<thin>\n"
|
||||
armagb = "<bigaf>\n"
|
||||
)
|
||||
|
||||
// Offsets and sizes for fields in a standard archive header.
|
||||
const (
|
||||
arNameOff = 0
|
||||
arNameSize = 16
|
||||
arDateOff = arNameOff + arNameSize
|
||||
arDateSize = 12
|
||||
arUIDOff = arDateOff + arDateSize
|
||||
arUIDSize = 6
|
||||
arGIDOff = arUIDOff + arUIDSize
|
||||
arGIDSize = 6
|
||||
arModeOff = arGIDOff + arGIDSize
|
||||
arModeSize = 8
|
||||
arSizeOff = arModeOff + arModeSize
|
||||
arSizeSize = 10
|
||||
arFmagOff = arSizeOff + arSizeSize
|
||||
arFmagSize = 2
|
||||
|
||||
arHdrSize = arFmagOff + arFmagSize
|
||||
)
|
||||
|
||||
// The contents of the fmag field of a standard archive header.
|
||||
const arfmag = "`\n"
|
||||
|
||||
// arExportData takes an archive file and returns a ReadSeeker for the
|
||||
// export data in that file. This assumes that there is only one
|
||||
// object in the archive containing export data, which is not quite
|
||||
// what gccgo does; gccgo concatenates together all the export data
|
||||
// for all the objects in the file. In practice that case does not arise.
|
||||
func arExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
|
||||
if _, err := archive.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf [len(armag)]byte
|
||||
if _, err := archive.Read(buf[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch string(buf[:]) {
|
||||
case armag:
|
||||
return standardArExportData(archive)
|
||||
case armagt:
|
||||
return nil, errors.New("unsupported thin archive")
|
||||
case armagb:
|
||||
return nil, errors.New("unsupported AIX big archive")
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized archive file format %q", buf[:])
|
||||
}
|
||||
}
|
||||
|
||||
// standardArExportData returns export data form a standard archive.
|
||||
func standardArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
|
||||
off := int64(len(armag))
|
||||
for {
|
||||
var hdrBuf [arHdrSize]byte
|
||||
if _, err := archive.Read(hdrBuf[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
off += arHdrSize
|
||||
|
||||
if bytes.Compare(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) != 0 {
|
||||
return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:])
|
||||
}
|
||||
|
||||
size, err := strconv.ParseInt(strings.TrimSpace(string(hdrBuf[arSizeOff:arSizeOff+arSizeSize])), 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing size in archive header (%q): %v", hdrBuf[:], err)
|
||||
}
|
||||
|
||||
fn := hdrBuf[arNameOff : arNameOff+arNameSize]
|
||||
if fn[0] == '/' && (fn[1] == ' ' || fn[1] == '/' || bytes.Compare(fn[:8], []byte("/SYM64/ ")) == 0) {
|
||||
// Archive symbol table or extended name table,
|
||||
// which we don't care about.
|
||||
} else {
|
||||
archiveAt := readerAtFromSeeker(archive)
|
||||
ret, err := elfFromAr(io.NewSectionReader(archiveAt, off, size))
|
||||
if ret != nil || err != nil {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
|
||||
if size&1 != 0 {
|
||||
size++
|
||||
}
|
||||
off += size
|
||||
if _, err := archive.Seek(off, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// elfFromAr tries to get export data from an archive member as an ELF file.
|
||||
// If there is no export data, this returns nil, nil.
|
||||
func elfFromAr(member *io.SectionReader) (io.ReadSeeker, error) {
|
||||
ef, err := elf.NewFile(member)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sec := ef.Section(".go_export")
|
||||
if sec == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return sec.Open(), nil
|
||||
}
|
||||
|
||||
// readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt.
|
||||
// This is only safe because there won't be any concurrent seeks
|
||||
// while this code is executing.
|
||||
func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt {
|
||||
if ret, ok := rs.(io.ReaderAt); ok {
|
||||
return ret
|
||||
}
|
||||
return seekerReadAt{rs}
|
||||
}
|
||||
|
||||
type seekerReadAt struct {
|
||||
seeker io.ReadSeeker
|
||||
}
|
||||
|
||||
func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) {
|
||||
if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return sra.seeker.Read(p)
|
||||
}
|
@ -6,13 +6,11 @@
|
||||
package gccgoimporter // import "go/internal/gccgoimporter"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
@ -98,18 +96,8 @@ func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err e
|
||||
return
|
||||
|
||||
case archiveMagic:
|
||||
// TODO(pcc): Read the archive directly instead of using "ar".
|
||||
f.Close()
|
||||
closer = nil
|
||||
|
||||
cmd := exec.Command("ar", "p", fpath)
|
||||
var out []byte
|
||||
out, err = cmd.Output()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
elfreader = bytes.NewReader(out)
|
||||
reader, err = arExportData(f)
|
||||
return
|
||||
|
||||
default:
|
||||
elfreader = f
|
||||
@ -189,17 +177,24 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo
|
||||
reader = r
|
||||
}
|
||||
|
||||
var magic [4]byte
|
||||
_, err = reader.Read(magic[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = reader.Seek(0, io.SeekStart)
|
||||
var magics string
|
||||
magics, err = readMagic(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch string(magic[:]) {
|
||||
if magics == archiveMagic {
|
||||
reader, err = arExportData(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
magics, err = readMagic(reader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch magics {
|
||||
case gccgov1Magic, gccgov2Magic:
|
||||
var p parser
|
||||
p.init(fpath, reader, imports)
|
||||
@ -230,9 +225,22 @@ func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Impo
|
||||
// }
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unrecognized magic string: %q", string(magic[:]))
|
||||
err = fmt.Errorf("unrecognized magic string: %q", magics)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// readMagic reads the four bytes at the start of a ReadSeeker and
|
||||
// returns them as a string.
|
||||
func readMagic(reader io.ReadSeeker) (string, error) {
|
||||
var magic [4]byte
|
||||
if _, err := reader.Read(magic[:]); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := reader.Seek(0, io.SeekStart); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(magic[:]), nil
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ var importerTests = [...]importerTest{
|
||||
{pkgpath: "unicode", name: "IsUpper", want: "func IsUpper(r rune) bool"},
|
||||
{pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"},
|
||||
{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
|
||||
{pkgpath: "importsar", name: "Hello", want: "var Hello string"},
|
||||
{pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"},
|
||||
{pkgpath: "escapeinfo", name: "NewT", want: "func NewT(data []byte) *T"},
|
||||
}
|
||||
|
BIN
src/go/internal/gccgoimporter/testdata/libimportsar.a
vendored
Normal file
BIN
src/go/internal/gccgoimporter/testdata/libimportsar.a
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user