diff options
Diffstat (limited to 'src/cmd/internal/archive/read.go')
-rw-r--r-- | src/cmd/internal/archive/read.go | 253 |
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 -} |