github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/link/internal/ld/objfile.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 ld
     6  
     7  // Writing and reading of Go object files.
     8  //
     9  // Originally, Go object files were Plan 9 object files, but no longer.
    10  // Now they are more like standard object files, in that each symbol is defined
    11  // by an associated memory image (bytes) and a list of relocations to apply
    12  // during linking. We do not (yet?) use a standard file format, however.
    13  // For now, the format is chosen to be as simple as possible to read and write.
    14  // It may change for reasons of efficiency, or we may even switch to a
    15  // standard file format if there are compelling benefits to doing so.
    16  // See golang.org/s/go13linker for more background.
    17  //
    18  // The file format is:
    19  //
    20  //	- magic header: "\x00\x00go13ld"
    21  //	- byte 1 - version number
    22  //	- sequence of strings giving dependencies (imported packages)
    23  //	- empty string (marks end of sequence)
    24  //	- sequence of defined symbols
    25  //	- byte 0xff (marks end of sequence)
    26  //	- magic footer: "\xff\xffgo13ld"
    27  //
    28  // All integers are stored in a zigzag varint format.
    29  // See golang.org/s/go12symtab for a definition.
    30  //
    31  // Data blocks and strings are both stored as an integer
    32  // followed by that many bytes.
    33  //
    34  // A symbol reference is a string name followed by a version.
    35  // An empty name corresponds to a nil LSym* pointer.
    36  //
    37  // Each symbol is laid out as the following fields (taken from LSym*):
    38  //
    39  //	- byte 0xfe (sanity check for synchronization)
    40  //	- type [int]
    41  //	- name [string]
    42  //	- version [int]
    43  //	- flags [int]
    44  //		1 dupok
    45  //	- size [int]
    46  //	- gotype [symbol reference]
    47  //	- p [data block]
    48  //	- nr [int]
    49  //	- r [nr relocations, sorted by off]
    50  //
    51  // If type == STEXT, there are a few more fields:
    52  //
    53  //	- args [int]
    54  //	- locals [int]
    55  //	- nosplit [int]
    56  //	- flags [int]
    57  //		1 leaf
    58  //		2 C function
    59  //	- nlocal [int]
    60  //	- local [nlocal automatics]
    61  //	- pcln [pcln table]
    62  //
    63  // Each relocation has the encoding:
    64  //
    65  //	- off [int]
    66  //	- siz [int]
    67  //	- type [int]
    68  //	- add [int]
    69  //	- xadd [int]
    70  //	- sym [symbol reference]
    71  //	- xsym [symbol reference]
    72  //
    73  // Each local has the encoding:
    74  //
    75  //	- asym [symbol reference]
    76  //	- offset [int]
    77  //	- type [int]
    78  //	- gotype [symbol reference]
    79  //
    80  // The pcln table has the encoding:
    81  //
    82  //	- pcsp [data block]
    83  //	- pcfile [data block]
    84  //	- pcline [data block]
    85  //	- npcdata [int]
    86  //	- pcdata [npcdata data blocks]
    87  //	- nfuncdata [int]
    88  //	- funcdata [nfuncdata symbol references]
    89  //	- funcdatasym [nfuncdata ints]
    90  //	- nfile [int]
    91  //	- file [nfile symbol references]
    92  //
    93  // The file layout and meaning of type integers are architecture-independent.
    94  //
    95  // TODO(rsc): The file format is good for a first pass but needs work.
    96  //	- There are SymID in the object file that should really just be strings.
    97  //	- The actual symbol memory images are interlaced with the symbol
    98  //	  metadata. They should be separated, to reduce the I/O required to
    99  //	  load just the metadata.
   100  //	- The symbol references should be shortened, either with a symbol
   101  //	  table or by using a simple backward index to an earlier mentioned symbol.
   102  
   103  import (
   104  	"bytes"
   105  	"cmd/internal/obj"
   106  	"fmt"
   107  	"log"
   108  	"strconv"
   109  	"strings"
   110  )
   111  
   112  const (
   113  	startmagic = "\x00\x00go13ld"
   114  	endmagic   = "\xff\xffgo13ld"
   115  )
   116  
   117  func ldobjfile(ctxt *Link, f *obj.Biobuf, pkg string, length int64, pn string) {
   118  	start := obj.Boffset(f)
   119  	ctxt.Version++
   120  	var buf [8]uint8
   121  	obj.Bread(f, buf[:])
   122  	if string(buf[:]) != startmagic {
   123  		log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
   124  	}
   125  	c := obj.Bgetc(f)
   126  	if c != 1 {
   127  		log.Fatalf("%s: invalid file version number %d", pn, c)
   128  	}
   129  
   130  	var lib string
   131  	for {
   132  		lib = rdstring(f)
   133  		if lib == "" {
   134  			break
   135  		}
   136  		addlib(ctxt, pkg, pn, lib)
   137  	}
   138  
   139  	for {
   140  		c, err := f.Peek(1)
   141  		if err != nil {
   142  			log.Fatalf("%s: peeking: %v", pn, err)
   143  		}
   144  		if c[0] == 0xff {
   145  			break
   146  		}
   147  		readsym(ctxt, f, pkg, pn)
   148  	}
   149  
   150  	buf = [8]uint8{}
   151  	obj.Bread(f, buf[:])
   152  	if string(buf[:]) != endmagic {
   153  		log.Fatalf("%s: invalid file end", pn)
   154  	}
   155  
   156  	if obj.Boffset(f) != start+length {
   157  		log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(obj.Boffset(f)), int64(start+length))
   158  	}
   159  }
   160  
   161  var readsym_ndup int
   162  
   163  func readsym(ctxt *Link, f *obj.Biobuf, pkg string, pn string) {
   164  	if obj.Bgetc(f) != 0xfe {
   165  		log.Fatalf("readsym out of sync")
   166  	}
   167  	t := rdint(f)
   168  	name := expandpkg(rdstring(f), pkg)
   169  	v := rdint(f)
   170  	if v != 0 && v != 1 {
   171  		log.Fatalf("invalid symbol version %d", v)
   172  	}
   173  	flags := rdint(f)
   174  	dupok := flags & 1
   175  	local := false
   176  	if flags&2 != 0 {
   177  		local = true
   178  	}
   179  	size := rdint(f)
   180  	typ := rdsym(ctxt, f, pkg)
   181  	data := rddata(f)
   182  	nreloc := rdint(f)
   183  
   184  	if v != 0 {
   185  		v = ctxt.Version
   186  	}
   187  	s := Linklookup(ctxt, name, v)
   188  	var dup *LSym
   189  	if s.Type != 0 && s.Type != obj.SXREF {
   190  		if (t == obj.SDATA || t == obj.SBSS || t == obj.SNOPTRBSS) && len(data) == 0 && nreloc == 0 {
   191  			if s.Size < int64(size) {
   192  				s.Size = int64(size)
   193  			}
   194  			if typ != nil && s.Gotype == nil {
   195  				s.Gotype = typ
   196  			}
   197  			return
   198  		}
   199  
   200  		if (s.Type == obj.SDATA || s.Type == obj.SBSS || s.Type == obj.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 {
   201  			goto overwrite
   202  		}
   203  		if s.Type != obj.SBSS && s.Type != obj.SNOPTRBSS && dupok == 0 && s.Dupok == 0 {
   204  			log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, pn)
   205  		}
   206  		if len(s.P) > 0 {
   207  			dup = s
   208  			s = linknewsym(ctxt, ".dup", readsym_ndup)
   209  			readsym_ndup++ // scratch
   210  		}
   211  	}
   212  
   213  overwrite:
   214  	s.File = pkg
   215  	s.Dupok = uint8(dupok)
   216  	if t == obj.SXREF {
   217  		log.Fatalf("bad sxref")
   218  	}
   219  	if t == 0 {
   220  		log.Fatalf("missing type for %s in %s", name, pn)
   221  	}
   222  	if t == obj.SBSS && (s.Type == obj.SRODATA || s.Type == obj.SNOPTRBSS) {
   223  		t = int(s.Type)
   224  	}
   225  	s.Type = int16(t)
   226  	if s.Size < int64(size) {
   227  		s.Size = int64(size)
   228  	}
   229  	s.Local = local
   230  	if typ != nil { // if bss sym defined multiple times, take type from any one def
   231  		s.Gotype = typ
   232  	}
   233  	if dup != nil && typ != nil {
   234  		dup.Gotype = typ
   235  	}
   236  	s.P = data
   237  	s.P = s.P[:len(data)]
   238  	if nreloc > 0 {
   239  		s.R = make([]Reloc, nreloc)
   240  		s.R = s.R[:nreloc]
   241  		var r *Reloc
   242  		for i := 0; i < nreloc; i++ {
   243  			r = &s.R[i]
   244  			r.Off = rdint32(f)
   245  			r.Siz = rduint8(f)
   246  			r.Type = rdint32(f)
   247  			r.Add = rdint64(f)
   248  			rdint64(f) // Xadd, ignored
   249  			r.Sym = rdsym(ctxt, f, pkg)
   250  			rdsym(ctxt, f, pkg) // Xsym, ignored
   251  		}
   252  	}
   253  
   254  	if len(s.P) > 0 && dup != nil && len(dup.P) > 0 && strings.HasPrefix(s.Name, "gclocals·") {
   255  		// content-addressed garbage collection liveness bitmap symbol.
   256  		// double check for hash collisions.
   257  		if !bytes.Equal(s.P, dup.P) {
   258  			log.Fatalf("dupok hash collision for %s in %s and %s", s.Name, s.File, pn)
   259  		}
   260  	}
   261  
   262  	if s.Type == obj.STEXT {
   263  		s.Args = rdint32(f)
   264  		s.Locals = rdint32(f)
   265  		s.Nosplit = rduint8(f)
   266  		v := rdint(f)
   267  		s.Leaf = uint8(v & 1)
   268  		s.Cfunc = uint8(v & 2)
   269  		n := rdint(f)
   270  		var a *Auto
   271  		for i := 0; i < n; i++ {
   272  			a = new(Auto)
   273  			a.Asym = rdsym(ctxt, f, pkg)
   274  			a.Aoffset = rdint32(f)
   275  			a.Name = rdint16(f)
   276  			a.Gotype = rdsym(ctxt, f, pkg)
   277  			a.Link = s.Autom
   278  			s.Autom = a
   279  		}
   280  
   281  		s.Pcln = new(Pcln)
   282  		pc := s.Pcln
   283  		pc.Pcsp.P = rddata(f)
   284  		pc.Pcfile.P = rddata(f)
   285  		pc.Pcline.P = rddata(f)
   286  		n = rdint(f)
   287  		pc.Pcdata = make([]Pcdata, n)
   288  		pc.Npcdata = n
   289  		for i := 0; i < n; i++ {
   290  			pc.Pcdata[i].P = rddata(f)
   291  		}
   292  		n = rdint(f)
   293  		pc.Funcdata = make([]*LSym, n)
   294  		pc.Funcdataoff = make([]int64, n)
   295  		pc.Nfuncdata = n
   296  		for i := 0; i < n; i++ {
   297  			pc.Funcdata[i] = rdsym(ctxt, f, pkg)
   298  		}
   299  		for i := 0; i < n; i++ {
   300  			pc.Funcdataoff[i] = rdint64(f)
   301  		}
   302  		n = rdint(f)
   303  		pc.File = make([]*LSym, n)
   304  		pc.Nfile = n
   305  		for i := 0; i < n; i++ {
   306  			pc.File[i] = rdsym(ctxt, f, pkg)
   307  		}
   308  
   309  		if dup == nil {
   310  			if s.Onlist != 0 {
   311  				log.Fatalf("symbol %s listed multiple times", s.Name)
   312  			}
   313  			s.Onlist = 1
   314  			if ctxt.Etextp != nil {
   315  				ctxt.Etextp.Next = s
   316  			} else {
   317  				ctxt.Textp = s
   318  			}
   319  			ctxt.Etextp = s
   320  		}
   321  	}
   322  
   323  	if ctxt.Debugasm != 0 {
   324  		fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
   325  		if s.Version != 0 {
   326  			fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version)
   327  		}
   328  		if s.Type != 0 {
   329  			fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type)
   330  		}
   331  		if s.Dupok != 0 {
   332  			fmt.Fprintf(ctxt.Bso, "dupok ")
   333  		}
   334  		if s.Cfunc != 0 {
   335  			fmt.Fprintf(ctxt.Bso, "cfunc ")
   336  		}
   337  		if s.Nosplit != 0 {
   338  			fmt.Fprintf(ctxt.Bso, "nosplit ")
   339  		}
   340  		fmt.Fprintf(ctxt.Bso, "size=%d value=%d", int64(s.Size), int64(s.Value))
   341  		if s.Type == obj.STEXT {
   342  			fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals))
   343  		}
   344  		fmt.Fprintf(ctxt.Bso, "\n")
   345  		var c int
   346  		var j int
   347  		for i := 0; i < len(s.P); {
   348  			fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
   349  			for j = i; j < i+16 && j < len(s.P); j++ {
   350  				fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
   351  			}
   352  			for ; j < i+16; j++ {
   353  				fmt.Fprintf(ctxt.Bso, "   ")
   354  			}
   355  			fmt.Fprintf(ctxt.Bso, "  ")
   356  			for j = i; j < i+16 && j < len(s.P); j++ {
   357  				c = int(s.P[j])
   358  				if ' ' <= c && c <= 0x7e {
   359  					fmt.Fprintf(ctxt.Bso, "%c", c)
   360  				} else {
   361  					fmt.Fprintf(ctxt.Bso, ".")
   362  				}
   363  			}
   364  
   365  			fmt.Fprintf(ctxt.Bso, "\n")
   366  			i += 16
   367  		}
   368  
   369  		var r *Reloc
   370  		for i := 0; i < len(s.R); i++ {
   371  			r = &s.R[i]
   372  			fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, r.Sym.Name, int64(r.Add))
   373  		}
   374  	}
   375  }
   376  
   377  func rdint64(f *obj.Biobuf) int64 {
   378  	var c int
   379  
   380  	uv := uint64(0)
   381  	for shift := 0; ; shift += 7 {
   382  		if shift >= 64 {
   383  			log.Fatalf("corrupt input")
   384  		}
   385  		c = obj.Bgetc(f)
   386  		uv |= uint64(c&0x7F) << uint(shift)
   387  		if c&0x80 == 0 {
   388  			break
   389  		}
   390  	}
   391  
   392  	return int64(uv>>1) ^ (int64(uint64(uv)<<63) >> 63)
   393  }
   394  
   395  func rdint(f *obj.Biobuf) int {
   396  	n := rdint64(f)
   397  	if int64(int(n)) != n {
   398  		log.Panicf("%v out of range for int", n)
   399  	}
   400  	return int(n)
   401  }
   402  
   403  func rdint32(f *obj.Biobuf) int32 {
   404  	n := rdint64(f)
   405  	if int64(int32(n)) != n {
   406  		log.Panicf("%v out of range for int32", n)
   407  	}
   408  	return int32(n)
   409  }
   410  
   411  func rdint16(f *obj.Biobuf) int16 {
   412  	n := rdint64(f)
   413  	if int64(int16(n)) != n {
   414  		log.Panicf("%v out of range for int16", n)
   415  	}
   416  	return int16(n)
   417  }
   418  
   419  func rduint8(f *obj.Biobuf) uint8 {
   420  	n := rdint64(f)
   421  	if int64(uint8(n)) != n {
   422  		log.Panicf("%v out of range for uint8", n)
   423  	}
   424  	return uint8(n)
   425  }
   426  
   427  func rdstring(f *obj.Biobuf) string {
   428  	n := rdint64(f)
   429  	p := make([]byte, n)
   430  	obj.Bread(f, p)
   431  	return string(p)
   432  }
   433  
   434  func rddata(f *obj.Biobuf) []byte {
   435  	n := rdint64(f)
   436  	p := make([]byte, n)
   437  	obj.Bread(f, p)
   438  	return p
   439  }
   440  
   441  var symbuf []byte
   442  
   443  func rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym {
   444  	n := rdint(f)
   445  	if n == 0 {
   446  		rdint64(f)
   447  		return nil
   448  	}
   449  
   450  	if len(symbuf) < n {
   451  		symbuf = make([]byte, n)
   452  	}
   453  	obj.Bread(f, symbuf[:n])
   454  	p := string(symbuf[:n])
   455  	v := rdint(f)
   456  	if v != 0 {
   457  		v = ctxt.Version
   458  	}
   459  	s := Linklookup(ctxt, expandpkg(p, pkg), v)
   460  
   461  	if v == 0 && s.Name[0] == '$' && s.Type == 0 {
   462  		if strings.HasPrefix(s.Name, "$f32.") {
   463  			x, _ := strconv.ParseUint(s.Name[5:], 16, 32)
   464  			i32 := int32(x)
   465  			s.Type = obj.SRODATA
   466  			s.Local = true
   467  			Adduint32(ctxt, s, uint32(i32))
   468  			s.Reachable = false
   469  		} else if strings.HasPrefix(s.Name, "$f64.") || strings.HasPrefix(s.Name, "$i64.") {
   470  			x, _ := strconv.ParseUint(s.Name[5:], 16, 64)
   471  			i64 := int64(x)
   472  			s.Type = obj.SRODATA
   473  			s.Local = true
   474  			Adduint64(ctxt, s, uint64(i64))
   475  			s.Reachable = false
   476  		}
   477  	}
   478  	if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") {
   479  		s.Local = true
   480  	}
   481  	return s
   482  }