summaryrefslogtreecommitdiff
path: root/src/cmd/internal/archive/read.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/internal/archive/read.go')
-rw-r--r--src/cmd/internal/archive/read.go253
1 files changed, 60 insertions, 193 deletions
diff --git a/src/cmd/internal/archive/read.go b/src/cmd/internal/archive/read.go
index cb388a84cd..6875dbce75 100644
--- a/src/cmd/internal/archive/read.go
+++ b/src/cmd/internal/archive/read.go
@@ -2,58 +2,22 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package goobj implements reading of Go object files and archives.
-//
-// TODO(rsc): Decide where this package should live. (golang.org/issue/6932)
-// TODO(rsc): Decide the appropriate integer types for various fields.
-package goobj
+// Package archive implements reading of archive files generated by the Go
+// toolchain.
+package archive
import (
"bufio"
"bytes"
+ "cmd/internal/bio"
"cmd/internal/goobj2"
- "cmd/internal/objabi"
"errors"
"fmt"
"io"
"os"
"strconv"
- "strings"
)
-// A Sym is a named symbol in an object file.
-type Sym struct {
- SymID // symbol identifier (name and version)
- Kind objabi.SymKind // kind of symbol
- DupOK bool // are duplicate definitions okay?
- Size int64 // size of corresponding data
- Type SymID // symbol for Go type information
- Data Data // memory image of symbol
- Reloc []Reloc // relocations to apply to Data
- Func *Func // additional data for functions
-}
-
-// A SymID - the combination of Name and Version - uniquely identifies
-// a symbol within a package.
-type SymID struct {
- // Name is the name of a symbol.
- Name string
-
- // Version is zero for symbols with global visibility.
- // Symbols with only file visibility (such as file-level static
- // declarations in C) have a non-zero version distinguishing
- // a symbol in one file from a symbol of the same name
- // in another file
- Version int64
-}
-
-func (s SymID) String() string {
- if s.Version == 0 {
- return s.Name
- }
- return fmt.Sprintf("%s<%d>", s.Name, s.Version)
-}
-
// A Data is a reference to data stored in an object file.
// It records the offset and size of the data, so that a client can
// read the data only if necessary.
@@ -62,88 +26,29 @@ type Data struct {
Size int64
}
-// A Reloc describes a relocation applied to a memory image to refer
-// to an address within a particular symbol.
-type Reloc struct {
- // The bytes at [Offset, Offset+Size) within the containing Sym
- // should be updated to refer to the address Add bytes after the start
- // of the symbol Sym.
- Offset int64
- Size int64
- Sym SymID
- Add int64
-
- // The Type records the form of address expected in the bytes
- // described by the previous fields: absolute, PC-relative, and so on.
- // TODO(rsc): The interpretation of Type is not exposed by this package.
- Type objabi.RelocType
-}
-
-// A Var describes a variable in a function stack frame: a declared
-// local variable, an input argument, or an output result.
-type Var struct {
- // The combination of Name, Kind, and Offset uniquely
- // identifies a variable in a function stack frame.
- // Using fewer of these - in particular, using only Name - does not.
- Name string // Name of variable.
- Kind int64 // TODO(rsc): Define meaning.
- Offset int64 // Frame offset. TODO(rsc): Define meaning.
-
- Type SymID // Go type for variable.
+type Archive struct {
+ f *os.File
+ Entries []Entry
}
-// Func contains additional per-symbol information specific to functions.
-type Func struct {
- Args int64 // size in bytes of argument frame: inputs and outputs
- Frame int64 // size in bytes of local variable frame
- Align uint32 // alignment requirement in bytes for the address of the function
- Leaf bool // function omits save of link register (ARM)
- NoSplit bool // function omits stack split prologue
- TopFrame bool // function is the top of the call stack
- Var []Var // detail about local variables
- PCSP Data // PC → SP offset map
- PCFile Data // PC → file number map (index into File)
- PCLine Data // PC → line number map
- PCInline Data // PC → inline tree index map
- PCData []Data // PC → runtime support data map
- FuncData []FuncData // non-PC-specific runtime support data
- File map[goobj2.CUFileIndex]struct{} // set of files used in this function
- InlTree []InlinedCall
+type Entry struct {
+ Name string
+ Type EntryType
+ Data
+ Obj *GoObj // nil if this entry is not a Go object file
}
-// TODO: Add PCData []byte and PCDataIter (similar to liblink).
-
-// A FuncData is a single function-specific data value.
-type FuncData struct {
- Sym SymID // symbol holding data
- Offset int64 // offset into symbol for funcdata pointer
-}
+type EntryType int
-// An InlinedCall is a node in an InlTree.
-// See cmd/internal/obj.InlTree for details.
-type InlinedCall struct {
- Parent int64
- File goobj2.CUFileIndex
- Line int64
- Func SymID
- ParentPC int64
-}
+const (
+ EntryPkgDef EntryType = iota
+ EntryGoObj
+ EntryNativeObj
+)
-// A Package is a parsed Go object file or archive defining a Go package.
-type Package struct {
- ImportPath string // import path denoting this package
- Imports []string // packages imported by this package
- SymRefs []SymID // list of symbol names and versions referred to by this pack
- Syms []*Sym // symbols defined by this package
- MaxVersion int64 // maximum Version in any SymID in Syms
- Arch string // architecture
- Native []*NativeReader // native object data (e.g. ELF)
- FileList []string // List of files for this package.
-}
-
-type NativeReader struct {
- Name string
- io.ReaderAt
+type GoObj struct {
+ TextHeader []byte
+ Data
}
var (
@@ -159,26 +64,20 @@ var (
// An objReader is an object file reader.
type objReader struct {
- p *Package
- b *bufio.Reader
- f *os.File
- err error
- offset int64
- dataOffset int64
- limit int64
- tmp [256]byte
- pkgprefix string
+ a *Archive
+ b *bio.Reader
+ err error
+ offset int64
+ limit int64
+ tmp [256]byte
}
-// init initializes r to read package p from f.
-func (r *objReader) init(f *os.File, p *Package) {
- r.f = f
- r.p = p
+func (r *objReader) init(f *os.File) {
+ r.a = &Archive{f, nil}
r.offset, _ = f.Seek(0, io.SeekCurrent)
r.limit, _ = f.Seek(0, io.SeekEnd)
f.Seek(r.offset, io.SeekStart)
- r.b = bufio.NewReader(f)
- r.pkgprefix = objabi.PathToPrefix(p.ImportPath) + "."
+ r.b = bio.NewReader(f)
}
// error records that an error occurred.
@@ -278,27 +177,16 @@ func (r *objReader) skip(n int64) {
r.readFull(r.tmp[:n])
} else {
// Seek, giving up buffered data.
- _, err := r.f.Seek(r.offset+n, io.SeekStart)
- if err != nil {
- r.error(err)
- }
+ r.b.MustSeek(r.offset+n, io.SeekStart)
r.offset += n
- r.b.Reset(r.f)
}
}
-// Parse parses an object file or archive from f,
-// assuming that its import path is pkgpath.
-func Parse(f *os.File, pkgpath string) (*Package, error) {
- if pkgpath == "" {
- pkgpath = `""`
- }
- p := new(Package)
- p.ImportPath = pkgpath
-
- var rd objReader
- rd.init(f, p)
- err := rd.readFull(rd.tmp[:8])
+// Parse parses an object file or archive from f.
+func Parse(f *os.File) (*Archive, error) {
+ var r objReader
+ r.init(f)
+ t, err := r.peek(8)
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
@@ -310,17 +198,20 @@ func Parse(f *os.File, pkgpath string) (*Package, error) {
default:
return nil, errNotObject
- case bytes.Equal(rd.tmp[:8], archiveHeader):
- if err := rd.parseArchive(); err != nil {
+ case bytes.Equal(t, archiveHeader):
+ if err := r.parseArchive(); err != nil {
return nil, err
}
- case bytes.Equal(rd.tmp[:8], goobjHeader):
- if err := rd.parseObject(goobjHeader); err != nil {
+ case bytes.Equal(t, goobjHeader):
+ off := r.offset
+ o := &GoObj{}
+ if err := r.parseObject(o, r.limit-off); err != nil {
return nil, err
}
+ r.a.Entries = []Entry{{f.Name(), EntryGoObj, Data{off, r.limit - off}, o}}
}
- return p, nil
+ return r.a, nil
}
// trimSpace removes trailing spaces from b and returns the corresponding string.
@@ -331,6 +222,7 @@ func trimSpace(b []byte) string {
// parseArchive parses a Unix archive of Go object files.
func (r *objReader) parseArchive() error {
+ r.readFull(r.tmp[:8]) // consume header (already checked)
for r.offset < r.limit {
if err := r.readFull(r.tmp[:60]); err != nil {
return err
@@ -371,28 +263,25 @@ func (r *objReader) parseArchive() error {
}
switch name {
case "__.PKGDEF":
+ r.a.Entries = append(r.a.Entries, Entry{name, EntryPkgDef, Data{r.offset, size}, nil})
r.skip(size)
default:
- oldLimit := r.limit
- r.limit = r.offset + size
-
+ var typ EntryType
+ var o *GoObj
+ offset := r.offset
p, err := r.peek(8)
if err != nil {
return err
}
if bytes.Equal(p, goobjHeader) {
- if err := r.parseObject(nil); err != nil {
- return fmt.Errorf("parsing archive member %q: %v", name, err)
- }
+ typ = EntryGoObj
+ o = &GoObj{}
+ r.parseObject(o, size)
} else {
- r.p.Native = append(r.p.Native, &NativeReader{
- Name: name,
- ReaderAt: io.NewSectionReader(r.f, r.offset, size),
- })
+ typ = EntryNativeObj
+ r.skip(size)
}
-
- r.skip(r.limit - r.offset)
- r.limit = oldLimit
+ r.a.Entries = append(r.a.Entries, Entry{name, typ, Data{offset, size}, o})
}
if size&1 != 0 {
r.skip(1)
@@ -402,16 +291,12 @@ func (r *objReader) parseArchive() error {
}
// parseObject parses a single Go object file.
-// The prefix is the bytes already read from the file,
-// typically in order to detect that this is an object file.
// The object file consists of a textual header ending in "\n!\n"
// and then the part we want to parse begins.
// The format of that part is defined in a comment at the top
// of src/liblink/objfile.c.
-func (r *objReader) parseObject(prefix []byte) error {
- r.p.MaxVersion++
+func (r *objReader) parseObject(o *GoObj, size int64) error {
h := make([]byte, 0, 256)
- h = append(h, prefix...)
var c1, c2, c3 byte
for {
c1, c2, c3 = c2, c3, r.readByte()
@@ -425,12 +310,9 @@ func (r *objReader) parseObject(prefix []byte) error {
break
}
}
-
- hs := strings.Fields(string(h))
- if len(hs) >= 4 {
- r.p.Arch = hs[3]
- }
- // TODO: extract OS + build ID if/when we need it
+ o.TextHeader = h
+ o.Offset = r.offset
+ o.Size = size - int64(len(h))
p, err := r.peek(8)
if err != nil {
@@ -439,21 +321,6 @@ func (r *objReader) parseObject(prefix []byte) error {
if !bytes.Equal(p, []byte(goobj2.Magic)) {
return r.error(errCorruptObject)
}
- r.readNew()
+ r.skip(o.Size)
return nil
}
-
-func (r *Reloc) String(insnOffset uint64) string {
- delta := r.Offset - int64(insnOffset)
- s := fmt.Sprintf("[%d:%d]%s", delta, delta+r.Size, r.Type)
- if r.Sym.Name != "" {
- if r.Add != 0 {
- return fmt.Sprintf("%s:%s+%d", s, r.Sym.Name, r.Add)
- }
- return fmt.Sprintf("%s:%s", s, r.Sym.Name)
- }
- if r.Add != 0 {
- return fmt.Sprintf("%s:%d", s, r.Add)
- }
- return s
-}