github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/internal/goobj/read.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package goobj implements reading of Go object files and archives.
     6  //
     7  // TODO(rsc): Decide where this package should live. (golang.org/issue/6932)
     8  // TODO(rsc): Decide the appropriate integer types for various fields.
     9  // TODO(rsc): Write tests. (File format still up in the air a little.)
    10  package goobj
    11  
    12  import (
    13  	"bufio"
    14  	"bytes"
    15  	"cmd/internal/obj"
    16  	"errors"
    17  	"fmt"
    18  	"io"
    19  	"strconv"
    20  	"strings"
    21  )
    22  
    23  // A SymKind describes the kind of memory represented by a symbol.
    24  type SymKind int
    25  
    26  // This list is taken from include/link.h.
    27  
    28  // Defined SymKind values.
    29  // TODO(rsc): Give idiomatic Go names.
    30  // TODO(rsc): Reduce the number of symbol types in the object files.
    31  const (
    32  	_ SymKind = iota
    33  
    34  	// readonly, executable
    35  	STEXT      SymKind = obj.STEXT
    36  	SELFRXSECT SymKind = obj.SELFRXSECT
    37  
    38  	// readonly, non-executable
    39  	STYPE      SymKind = obj.STYPE
    40  	SSTRING    SymKind = obj.SSTRING
    41  	SGOSTRING  SymKind = obj.SGOSTRING
    42  	SGOFUNC    SymKind = obj.SGOFUNC
    43  	SRODATA    SymKind = obj.SRODATA
    44  	SFUNCTAB   SymKind = obj.SFUNCTAB
    45  	STYPELINK  SymKind = obj.STYPELINK
    46  	SITABLINK  SymKind = obj.SITABLINK
    47  	SSYMTAB    SymKind = obj.SSYMTAB // TODO: move to unmapped section
    48  	SPCLNTAB   SymKind = obj.SPCLNTAB
    49  	SELFROSECT SymKind = obj.SELFROSECT
    50  
    51  	// writable, non-executable
    52  	SMACHOPLT  SymKind = obj.SMACHOPLT
    53  	SELFSECT   SymKind = obj.SELFSECT
    54  	SMACHO     SymKind = obj.SMACHO // Mach-O __nl_symbol_ptr
    55  	SMACHOGOT  SymKind = obj.SMACHOGOT
    56  	SWINDOWS   SymKind = obj.SWINDOWS
    57  	SELFGOT    SymKind = obj.SELFGOT
    58  	SNOPTRDATA SymKind = obj.SNOPTRDATA
    59  	SINITARR   SymKind = obj.SINITARR
    60  	SDATA      SymKind = obj.SDATA
    61  	SBSS       SymKind = obj.SBSS
    62  	SNOPTRBSS  SymKind = obj.SNOPTRBSS
    63  	STLSBSS    SymKind = obj.STLSBSS
    64  
    65  	// not mapped
    66  	SXREF             SymKind = obj.SXREF
    67  	SMACHOSYMSTR      SymKind = obj.SMACHOSYMSTR
    68  	SMACHOSYMTAB      SymKind = obj.SMACHOSYMTAB
    69  	SMACHOINDIRECTPLT SymKind = obj.SMACHOINDIRECTPLT
    70  	SMACHOINDIRECTGOT SymKind = obj.SMACHOINDIRECTGOT
    71  	SFILE             SymKind = obj.SFILE
    72  	SFILEPATH         SymKind = obj.SFILEPATH
    73  	SCONST            SymKind = obj.SCONST
    74  	SDYNIMPORT        SymKind = obj.SDYNIMPORT
    75  	SHOSTOBJ          SymKind = obj.SHOSTOBJ
    76  )
    77  
    78  var symKindStrings = []string{
    79  	SBSS:              "SBSS",
    80  	SCONST:            "SCONST",
    81  	SDATA:             "SDATA",
    82  	SDYNIMPORT:        "SDYNIMPORT",
    83  	SELFROSECT:        "SELFROSECT",
    84  	SELFRXSECT:        "SELFRXSECT",
    85  	SELFSECT:          "SELFSECT",
    86  	SFILE:             "SFILE",
    87  	SFILEPATH:         "SFILEPATH",
    88  	SFUNCTAB:          "SFUNCTAB",
    89  	SGOFUNC:           "SGOFUNC",
    90  	SGOSTRING:         "SGOSTRING",
    91  	SHOSTOBJ:          "SHOSTOBJ",
    92  	SINITARR:          "SINITARR",
    93  	SMACHO:            "SMACHO",
    94  	SMACHOGOT:         "SMACHOGOT",
    95  	SMACHOINDIRECTGOT: "SMACHOINDIRECTGOT",
    96  	SMACHOINDIRECTPLT: "SMACHOINDIRECTPLT",
    97  	SMACHOPLT:         "SMACHOPLT",
    98  	SMACHOSYMSTR:      "SMACHOSYMSTR",
    99  	SMACHOSYMTAB:      "SMACHOSYMTAB",
   100  	SNOPTRBSS:         "SNOPTRBSS",
   101  	SNOPTRDATA:        "SNOPTRDATA",
   102  	SPCLNTAB:          "SPCLNTAB",
   103  	SRODATA:           "SRODATA",
   104  	SSTRING:           "SSTRING",
   105  	SSYMTAB:           "SSYMTAB",
   106  	STEXT:             "STEXT",
   107  	STLSBSS:           "STLSBSS",
   108  	STYPE:             "STYPE",
   109  	STYPELINK:         "STYPELINK",
   110  	SITABLINK:         "SITABLINK",
   111  	SWINDOWS:          "SWINDOWS",
   112  	SXREF:             "SXREF",
   113  }
   114  
   115  func (k SymKind) String() string {
   116  	if k < 0 || int(k) >= len(symKindStrings) {
   117  		return fmt.Sprintf("SymKind(%d)", k)
   118  	}
   119  	return symKindStrings[k]
   120  }
   121  
   122  // A Sym is a named symbol in an object file.
   123  type Sym struct {
   124  	SymID         // symbol identifier (name and version)
   125  	Kind  SymKind // kind of symbol
   126  	DupOK bool    // are duplicate definitions okay?
   127  	Size  int     // size of corresponding data
   128  	Type  SymID   // symbol for Go type information
   129  	Data  Data    // memory image of symbol
   130  	Reloc []Reloc // relocations to apply to Data
   131  	Func  *Func   // additional data for functions
   132  }
   133  
   134  // A SymID - the combination of Name and Version - uniquely identifies
   135  // a symbol within a package.
   136  type SymID struct {
   137  	// Name is the name of a symbol.
   138  	Name string
   139  
   140  	// Version is zero for symbols with global visibility.
   141  	// Symbols with only file visibility (such as file-level static
   142  	// declarations in C) have a non-zero version distinguishing
   143  	// a symbol in one file from a symbol of the same name
   144  	// in another file
   145  	Version int
   146  }
   147  
   148  func (s SymID) String() string {
   149  	if s.Version == 0 {
   150  		return s.Name
   151  	}
   152  	return fmt.Sprintf("%s<%d>", s.Name, s.Version)
   153  }
   154  
   155  // A Data is a reference to data stored in an object file.
   156  // It records the offset and size of the data, so that a client can
   157  // read the data only if necessary.
   158  type Data struct {
   159  	Offset int64
   160  	Size   int64
   161  }
   162  
   163  // A Reloc describes a relocation applied to a memory image to refer
   164  // to an address within a particular symbol.
   165  type Reloc struct {
   166  	// The bytes at [Offset, Offset+Size) within the memory image
   167  	// should be updated to refer to the address Add bytes after the start
   168  	// of the symbol Sym.
   169  	Offset int
   170  	Size   int
   171  	Sym    SymID
   172  	Add    int
   173  
   174  	// The Type records the form of address expected in the bytes
   175  	// described by the previous fields: absolute, PC-relative, and so on.
   176  	// TODO(rsc): The interpretation of Type is not exposed by this package.
   177  	Type int
   178  }
   179  
   180  // A Var describes a variable in a function stack frame: a declared
   181  // local variable, an input argument, or an output result.
   182  type Var struct {
   183  	// The combination of Name, Kind, and Offset uniquely
   184  	// identifies a variable in a function stack frame.
   185  	// Using fewer of these - in particular, using only Name - does not.
   186  	Name   string // Name of variable.
   187  	Kind   int    // TODO(rsc): Define meaning.
   188  	Offset int    // Frame offset. TODO(rsc): Define meaning.
   189  
   190  	Type SymID // Go type for variable.
   191  }
   192  
   193  // Func contains additional per-symbol information specific to functions.
   194  type Func struct {
   195  	Args     int        // size in bytes of argument frame: inputs and outputs
   196  	Frame    int        // size in bytes of local variable frame
   197  	Leaf     bool       // function omits save of link register (ARM)
   198  	NoSplit  bool       // function omits stack split prologue
   199  	Var      []Var      // detail about local variables
   200  	PCSP     Data       // PC → SP offset map
   201  	PCFile   Data       // PC → file number map (index into File)
   202  	PCLine   Data       // PC → line number map
   203  	PCData   []Data     // PC → runtime support data map
   204  	FuncData []FuncData // non-PC-specific runtime support data
   205  	File     []string   // paths indexed by PCFile
   206  }
   207  
   208  // TODO: Add PCData []byte and PCDataIter (similar to liblink).
   209  
   210  // A FuncData is a single function-specific data value.
   211  type FuncData struct {
   212  	Sym    SymID // symbol holding data
   213  	Offset int64 // offset into symbol for funcdata pointer
   214  }
   215  
   216  // A Package is a parsed Go object file or archive defining a Go package.
   217  type Package struct {
   218  	ImportPath string   // import path denoting this package
   219  	Imports    []string // packages imported by this package
   220  	SymRefs    []SymID  // list of symbol names and versions referred to by this pack
   221  	Syms       []*Sym   // symbols defined by this package
   222  	MaxVersion int      // maximum Version in any SymID in Syms
   223  }
   224  
   225  var (
   226  	archiveHeader = []byte("!<arch>\n")
   227  	archiveMagic  = []byte("`\n")
   228  	goobjHeader   = []byte("go objec") // truncated to size of archiveHeader
   229  
   230  	errCorruptArchive   = errors.New("corrupt archive")
   231  	errTruncatedArchive = errors.New("truncated archive")
   232  	errNotArchive       = errors.New("unrecognized archive format")
   233  
   234  	errCorruptObject   = errors.New("corrupt object file")
   235  	errTruncatedObject = errors.New("truncated object file")
   236  	errNotObject       = errors.New("unrecognized object file format")
   237  )
   238  
   239  // An objReader is an object file reader.
   240  type objReader struct {
   241  	p          *Package
   242  	b          *bufio.Reader
   243  	f          io.ReadSeeker
   244  	err        error
   245  	offset     int64
   246  	dataOffset int64
   247  	limit      int64
   248  	tmp        [256]byte
   249  	pkg        string
   250  	pkgprefix  string
   251  }
   252  
   253  // importPathToPrefix returns the prefix that will be used in the
   254  // final symbol table for the given import path.
   255  // We escape '%', '"', all control characters and non-ASCII bytes,
   256  // and any '.' after the final slash.
   257  //
   258  // See ../../../cmd/ld/lib.c:/^pathtoprefix and
   259  // ../../../cmd/gc/subr.c:/^pathtoprefix.
   260  func importPathToPrefix(s string) string {
   261  	// find index of last slash, if any, or else -1.
   262  	// used for determining whether an index is after the last slash.
   263  	slash := strings.LastIndex(s, "/")
   264  
   265  	// check for chars that need escaping
   266  	n := 0
   267  	for r := 0; r < len(s); r++ {
   268  		if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
   269  			n++
   270  		}
   271  	}
   272  
   273  	// quick exit
   274  	if n == 0 {
   275  		return s
   276  	}
   277  
   278  	// escape
   279  	const hex = "0123456789abcdef"
   280  	p := make([]byte, 0, len(s)+2*n)
   281  	for r := 0; r < len(s); r++ {
   282  		if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
   283  			p = append(p, '%', hex[c>>4], hex[c&0xF])
   284  		} else {
   285  			p = append(p, c)
   286  		}
   287  	}
   288  
   289  	return string(p)
   290  }
   291  
   292  // init initializes r to read package p from f.
   293  func (r *objReader) init(f io.ReadSeeker, p *Package) {
   294  	r.f = f
   295  	r.p = p
   296  	r.offset, _ = f.Seek(0, 1)
   297  	r.limit, _ = f.Seek(0, 2)
   298  	f.Seek(r.offset, 0)
   299  	r.b = bufio.NewReader(f)
   300  	r.pkgprefix = importPathToPrefix(p.ImportPath) + "."
   301  }
   302  
   303  // error records that an error occurred.
   304  // It returns only the first error, so that an error
   305  // caused by an earlier error does not discard information
   306  // about the earlier error.
   307  func (r *objReader) error(err error) error {
   308  	if r.err == nil {
   309  		if err == io.EOF {
   310  			err = io.ErrUnexpectedEOF
   311  		}
   312  		r.err = err
   313  	}
   314  	// panic("corrupt") // useful for debugging
   315  	return r.err
   316  }
   317  
   318  // readByte reads and returns a byte from the input file.
   319  // On I/O error or EOF, it records the error but returns byte 0.
   320  // A sequence of 0 bytes will eventually terminate any
   321  // parsing state in the object file. In particular, it ends the
   322  // reading of a varint.
   323  func (r *objReader) readByte() byte {
   324  	if r.err != nil {
   325  		return 0
   326  	}
   327  	if r.offset >= r.limit {
   328  		r.error(io.ErrUnexpectedEOF)
   329  		return 0
   330  	}
   331  	b, err := r.b.ReadByte()
   332  	if err != nil {
   333  		if err == io.EOF {
   334  			err = io.ErrUnexpectedEOF
   335  		}
   336  		r.error(err)
   337  		b = 0
   338  	} else {
   339  		r.offset++
   340  	}
   341  	return b
   342  }
   343  
   344  // read reads exactly len(b) bytes from the input file.
   345  // If an error occurs, read returns the error but also
   346  // records it, so it is safe for callers to ignore the result
   347  // as long as delaying the report is not a problem.
   348  func (r *objReader) readFull(b []byte) error {
   349  	if r.err != nil {
   350  		return r.err
   351  	}
   352  	if r.offset+int64(len(b)) > r.limit {
   353  		return r.error(io.ErrUnexpectedEOF)
   354  	}
   355  	n, err := io.ReadFull(r.b, b)
   356  	r.offset += int64(n)
   357  	if err != nil {
   358  		return r.error(err)
   359  	}
   360  	return nil
   361  }
   362  
   363  // readInt reads a zigzag varint from the input file.
   364  func (r *objReader) readInt() int {
   365  	var u uint64
   366  
   367  	for shift := uint(0); ; shift += 7 {
   368  		if shift >= 64 {
   369  			r.error(errCorruptObject)
   370  			return 0
   371  		}
   372  		c := r.readByte()
   373  		u |= uint64(c&0x7F) << shift
   374  		if c&0x80 == 0 {
   375  			break
   376  		}
   377  	}
   378  
   379  	v := int64(u>>1) ^ (int64(u) << 63 >> 63)
   380  	if int64(int(v)) != v {
   381  		r.error(errCorruptObject) // TODO
   382  		return 0
   383  	}
   384  	return int(v)
   385  }
   386  
   387  // readString reads a length-delimited string from the input file.
   388  func (r *objReader) readString() string {
   389  	n := r.readInt()
   390  	buf := make([]byte, n)
   391  	r.readFull(buf)
   392  	return string(buf)
   393  }
   394  
   395  // readSymID reads a SymID from the input file.
   396  func (r *objReader) readSymID() SymID {
   397  	i := r.readInt()
   398  	return r.p.SymRefs[i]
   399  }
   400  
   401  func (r *objReader) readRef() {
   402  	name, vers := r.readString(), r.readInt()
   403  
   404  	// In a symbol name in an object file, "". denotes the
   405  	// prefix for the package in which the object file has been found.
   406  	// Expand it.
   407  	name = strings.Replace(name, `"".`, r.pkgprefix, -1)
   408  
   409  	// An individual object file only records version 0 (extern) or 1 (static).
   410  	// To make static symbols unique across all files being read, we
   411  	// replace version 1 with the version corresponding to the current
   412  	// file number. The number is incremented on each call to parseObject.
   413  	if vers != 0 {
   414  		vers = r.p.MaxVersion
   415  	}
   416  	r.p.SymRefs = append(r.p.SymRefs, SymID{name, vers})
   417  }
   418  
   419  // readData reads a data reference from the input file.
   420  func (r *objReader) readData() Data {
   421  	n := r.readInt()
   422  	d := Data{Offset: r.dataOffset, Size: int64(n)}
   423  	r.dataOffset += int64(n)
   424  	return d
   425  }
   426  
   427  // skip skips n bytes in the input.
   428  func (r *objReader) skip(n int64) {
   429  	if n < 0 {
   430  		r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip"))
   431  	}
   432  	if n < int64(len(r.tmp)) {
   433  		// Since the data is so small, a just reading from the buffered
   434  		// reader is better than flushing the buffer and seeking.
   435  		r.readFull(r.tmp[:n])
   436  	} else if n <= int64(r.b.Buffered()) {
   437  		// Even though the data is not small, it has already been read.
   438  		// Advance the buffer instead of seeking.
   439  		for n > int64(len(r.tmp)) {
   440  			r.readFull(r.tmp[:])
   441  			n -= int64(len(r.tmp))
   442  		}
   443  		r.readFull(r.tmp[:n])
   444  	} else {
   445  		// Seek, giving up buffered data.
   446  		_, err := r.f.Seek(r.offset+n, 0)
   447  		if err != nil {
   448  			r.error(err)
   449  		}
   450  		r.offset += n
   451  		r.b.Reset(r.f)
   452  	}
   453  }
   454  
   455  // Parse parses an object file or archive from r,
   456  // assuming that its import path is pkgpath.
   457  func Parse(r io.ReadSeeker, pkgpath string) (*Package, error) {
   458  	if pkgpath == "" {
   459  		pkgpath = `""`
   460  	}
   461  	p := new(Package)
   462  	p.ImportPath = pkgpath
   463  
   464  	var rd objReader
   465  	rd.init(r, p)
   466  	err := rd.readFull(rd.tmp[:8])
   467  	if err != nil {
   468  		if err == io.EOF {
   469  			err = io.ErrUnexpectedEOF
   470  		}
   471  		return nil, err
   472  	}
   473  
   474  	switch {
   475  	default:
   476  		return nil, errNotObject
   477  
   478  	case bytes.Equal(rd.tmp[:8], archiveHeader):
   479  		if err := rd.parseArchive(); err != nil {
   480  			return nil, err
   481  		}
   482  	case bytes.Equal(rd.tmp[:8], goobjHeader):
   483  		if err := rd.parseObject(goobjHeader); err != nil {
   484  			return nil, err
   485  		}
   486  	}
   487  
   488  	return p, nil
   489  }
   490  
   491  // trimSpace removes trailing spaces from b and returns the corresponding string.
   492  // This effectively parses the form used in archive headers.
   493  func trimSpace(b []byte) string {
   494  	return string(bytes.TrimRight(b, " "))
   495  }
   496  
   497  // parseArchive parses a Unix archive of Go object files.
   498  // TODO(rsc): Need to skip non-Go object files.
   499  // TODO(rsc): Maybe record table of contents in r.p so that
   500  // linker can avoid having code to parse archives too.
   501  func (r *objReader) parseArchive() error {
   502  	for r.offset < r.limit {
   503  		if err := r.readFull(r.tmp[:60]); err != nil {
   504  			return err
   505  		}
   506  		data := r.tmp[:60]
   507  
   508  		// Each file is preceded by this text header (slice indices in first column):
   509  		//	 0:16	name
   510  		//	16:28 date
   511  		//	28:34 uid
   512  		//	34:40 gid
   513  		//	40:48 mode
   514  		//	48:58 size
   515  		//	58:60 magic - `\n
   516  		// We only care about name, size, and magic.
   517  		// The fields are space-padded on the right.
   518  		// The size is in decimal.
   519  		// The file data - size bytes - follows the header.
   520  		// Headers are 2-byte aligned, so if size is odd, an extra padding
   521  		// byte sits between the file data and the next header.
   522  		// The file data that follows is padded to an even number of bytes:
   523  		// if size is odd, an extra padding byte is inserted betw the next header.
   524  		if len(data) < 60 {
   525  			return errTruncatedArchive
   526  		}
   527  		if !bytes.Equal(data[58:60], archiveMagic) {
   528  			return errCorruptArchive
   529  		}
   530  		name := trimSpace(data[0:16])
   531  		size, err := strconv.ParseInt(trimSpace(data[48:58]), 10, 64)
   532  		if err != nil {
   533  			return errCorruptArchive
   534  		}
   535  		data = data[60:]
   536  		fsize := size + size&1
   537  		if fsize < 0 || fsize < size {
   538  			return errCorruptArchive
   539  		}
   540  		switch name {
   541  		case "__.PKGDEF":
   542  			r.skip(size)
   543  		default:
   544  			oldLimit := r.limit
   545  			r.limit = r.offset + size
   546  			if err := r.parseObject(nil); err != nil {
   547  				return fmt.Errorf("parsing archive member %q: %v", name, err)
   548  			}
   549  			r.skip(r.limit - r.offset)
   550  			r.limit = oldLimit
   551  		}
   552  		if size&1 != 0 {
   553  			r.skip(1)
   554  		}
   555  	}
   556  	return nil
   557  }
   558  
   559  // parseObject parses a single Go object file.
   560  // The prefix is the bytes already read from the file,
   561  // typically in order to detect that this is an object file.
   562  // The object file consists of a textual header ending in "\n!\n"
   563  // and then the part we want to parse begins.
   564  // The format of that part is defined in a comment at the top
   565  // of src/liblink/objfile.c.
   566  func (r *objReader) parseObject(prefix []byte) error {
   567  	// TODO(rsc): Maybe use prefix and the initial input to
   568  	// record the header line from the file, which would
   569  	// give the architecture and other version information.
   570  
   571  	r.p.MaxVersion++
   572  	var c1, c2, c3 byte
   573  	for {
   574  		c1, c2, c3 = c2, c3, r.readByte()
   575  		// The new export format can contain 0 bytes.
   576  		// Don't consider them errors, only look for r.err != nil.
   577  		if r.err != nil {
   578  			return errCorruptObject
   579  		}
   580  		if c1 == '\n' && c2 == '!' && c3 == '\n' {
   581  			break
   582  		}
   583  	}
   584  
   585  	r.readFull(r.tmp[:8])
   586  	if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go13ld")) {
   587  		return r.error(errCorruptObject)
   588  	}
   589  
   590  	b := r.readByte()
   591  	if b != 1 {
   592  		return r.error(errCorruptObject)
   593  	}
   594  
   595  	// Direct package dependencies.
   596  	for {
   597  		s := r.readString()
   598  		if s == "" {
   599  			break
   600  		}
   601  		r.p.Imports = append(r.p.Imports, s)
   602  	}
   603  
   604  	r.p.SymRefs = []SymID{{"", 0}}
   605  	for {
   606  		if b := r.readByte(); b != 0xfe {
   607  			if b != 0xff {
   608  				return r.error(errCorruptObject)
   609  			}
   610  			break
   611  		}
   612  
   613  		r.readRef()
   614  	}
   615  
   616  	dataLength := r.readInt()
   617  	r.readInt() // n relocations - ignore
   618  	r.readInt() // n pcdata - ignore
   619  	r.readInt() // n autom - ignore
   620  	r.readInt() // n funcdata - ignore
   621  	r.readInt() // n files - ignore
   622  
   623  	r.dataOffset = r.offset
   624  	r.skip(int64(dataLength))
   625  
   626  	// Symbols.
   627  	for {
   628  		if b := r.readByte(); b != 0xfe {
   629  			if b != 0xff {
   630  				return r.error(errCorruptObject)
   631  			}
   632  			break
   633  		}
   634  
   635  		typ := r.readInt()
   636  		s := &Sym{SymID: r.readSymID()}
   637  		r.p.Syms = append(r.p.Syms, s)
   638  		s.Kind = SymKind(typ)
   639  		flags := r.readInt()
   640  		s.DupOK = flags&1 != 0
   641  		s.Size = r.readInt()
   642  		s.Type = r.readSymID()
   643  		s.Data = r.readData()
   644  		s.Reloc = make([]Reloc, r.readInt())
   645  		for i := range s.Reloc {
   646  			rel := &s.Reloc[i]
   647  			rel.Offset = r.readInt()
   648  			rel.Size = r.readInt()
   649  			rel.Type = r.readInt()
   650  			rel.Add = r.readInt()
   651  			rel.Sym = r.readSymID()
   652  		}
   653  
   654  		if s.Kind == STEXT {
   655  			f := new(Func)
   656  			s.Func = f
   657  			f.Args = r.readInt()
   658  			f.Frame = r.readInt()
   659  			flags := r.readInt()
   660  			f.Leaf = flags&1 != 0
   661  			f.NoSplit = r.readInt() != 0
   662  			f.Var = make([]Var, r.readInt())
   663  			for i := range f.Var {
   664  				v := &f.Var[i]
   665  				v.Name = r.readSymID().Name
   666  				v.Offset = r.readInt()
   667  				v.Kind = r.readInt()
   668  				v.Type = r.readSymID()
   669  			}
   670  
   671  			f.PCSP = r.readData()
   672  			f.PCFile = r.readData()
   673  			f.PCLine = r.readData()
   674  			f.PCData = make([]Data, r.readInt())
   675  			for i := range f.PCData {
   676  				f.PCData[i] = r.readData()
   677  			}
   678  			f.FuncData = make([]FuncData, r.readInt())
   679  			for i := range f.FuncData {
   680  				f.FuncData[i].Sym = r.readSymID()
   681  			}
   682  			for i := range f.FuncData {
   683  				f.FuncData[i].Offset = int64(r.readInt()) // TODO
   684  			}
   685  			f.File = make([]string, r.readInt())
   686  			for i := range f.File {
   687  				f.File[i] = r.readSymID().Name
   688  			}
   689  		}
   690  	}
   691  
   692  	r.readFull(r.tmp[:7])
   693  	if !bytes.Equal(r.tmp[:7], []byte("\xffgo13ld")) {
   694  		return r.error(errCorruptObject)
   695  	}
   696  
   697  	return nil
   698  }