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