github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/gc/obj.go (about)

     1  // Copyright 2009 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 gc
     6  
     7  import (
     8  	"cmd/internal/bio"
     9  	"cmd/internal/obj"
    10  	"crypto/sha256"
    11  	"fmt"
    12  	"io"
    13  	"strconv"
    14  )
    15  
    16  // architecture-independent object file output
    17  const (
    18  	ArhdrSize = 60
    19  )
    20  
    21  func formathdr(arhdr []byte, name string, size int64) {
    22  	copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
    23  }
    24  
    25  // These modes say which kind of object file to generate.
    26  // The default use of the toolchain is to set both bits,
    27  // generating a combined compiler+linker object, one that
    28  // serves to describe the package to both the compiler and the linker.
    29  // In fact the compiler and linker read nearly disjoint sections of
    30  // that file, though, so in a distributed build setting it can be more
    31  // efficient to split the output into two files, supplying the compiler
    32  // object only to future compilations and the linker object only to
    33  // future links.
    34  //
    35  // By default a combined object is written, but if -linkobj is specified
    36  // on the command line then the default -o output is a compiler object
    37  // and the -linkobj output is a linker object.
    38  const (
    39  	modeCompilerObj = 1 << iota
    40  	modeLinkerObj
    41  )
    42  
    43  func dumpobj() {
    44  	if linkobj == "" {
    45  		dumpobj1(outfile, modeCompilerObj|modeLinkerObj)
    46  	} else {
    47  		dumpobj1(outfile, modeCompilerObj)
    48  		dumpobj1(linkobj, modeLinkerObj)
    49  	}
    50  }
    51  
    52  func dumpobj1(outfile string, mode int) {
    53  	var err error
    54  	bout, err = bio.Create(outfile)
    55  	if err != nil {
    56  		flusherrors()
    57  		fmt.Printf("can't create %s: %v\n", outfile, err)
    58  		errorexit()
    59  	}
    60  
    61  	startobj := int64(0)
    62  	var arhdr [ArhdrSize]byte
    63  	if writearchive {
    64  		bout.WriteString("!<arch>\n")
    65  		arhdr = [ArhdrSize]byte{}
    66  		bout.Write(arhdr[:])
    67  		startobj = bout.Offset()
    68  	}
    69  
    70  	printheader := func() {
    71  		fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.GOOS, obj.GOARCH, obj.Version, obj.Expstring())
    72  		if buildid != "" {
    73  			fmt.Fprintf(bout, "build id %q\n", buildid)
    74  		}
    75  		if localpkg.Name == "main" {
    76  			fmt.Fprintf(bout, "main\n")
    77  		}
    78  		if safemode {
    79  			fmt.Fprintf(bout, "safe\n")
    80  		} else {
    81  			fmt.Fprintf(bout, "----\n") // room for some other tool to write "safe"
    82  		}
    83  		fmt.Fprintf(bout, "\n") // header ends with blank line
    84  	}
    85  
    86  	printheader()
    87  
    88  	if mode&modeCompilerObj != 0 {
    89  		dumpexport()
    90  	}
    91  
    92  	if writearchive {
    93  		bout.Flush()
    94  		size := bout.Offset() - startobj
    95  		if size&1 != 0 {
    96  			bout.WriteByte(0)
    97  		}
    98  		bout.Seek(startobj-ArhdrSize, 0)
    99  		formathdr(arhdr[:], "__.PKGDEF", size)
   100  		bout.Write(arhdr[:])
   101  		bout.Flush()
   102  		bout.Seek(startobj+size+(size&1), 0)
   103  	}
   104  
   105  	if mode&modeLinkerObj == 0 {
   106  		bout.Close()
   107  		return
   108  	}
   109  
   110  	if writearchive {
   111  		// start object file
   112  		arhdr = [ArhdrSize]byte{}
   113  		bout.Write(arhdr[:])
   114  		startobj = bout.Offset()
   115  		printheader()
   116  	}
   117  
   118  	if pragcgobuf != "" {
   119  		if writearchive {
   120  			// write empty export section; must be before cgo section
   121  			fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
   122  		}
   123  
   124  		fmt.Fprintf(bout, "\n$$  // cgo\n")
   125  		fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf)
   126  	}
   127  
   128  	fmt.Fprintf(bout, "\n!\n")
   129  
   130  	externs := len(externdcl)
   131  
   132  	dumpglobls()
   133  	dumpptabs()
   134  	dumptypestructs()
   135  
   136  	// Dump extra globals.
   137  	tmp := externdcl
   138  
   139  	if externdcl != nil {
   140  		externdcl = externdcl[externs:]
   141  	}
   142  	dumpglobls()
   143  	externdcl = tmp
   144  
   145  	if zerosize > 0 {
   146  		zero := Pkglookup("zero", mappkg)
   147  		ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA)
   148  	}
   149  
   150  	obj.Writeobjdirect(Ctxt, bout.Writer)
   151  
   152  	if writearchive {
   153  		bout.Flush()
   154  		size := bout.Offset() - startobj
   155  		if size&1 != 0 {
   156  			bout.WriteByte(0)
   157  		}
   158  		bout.Seek(startobj-ArhdrSize, 0)
   159  		formathdr(arhdr[:], "_go_.o", size)
   160  		bout.Write(arhdr[:])
   161  	}
   162  
   163  	bout.Close()
   164  }
   165  
   166  func dumpptabs() {
   167  	if !Ctxt.Flag_dynlink || localpkg.Name != "main" {
   168  		return
   169  	}
   170  	for _, exportn := range exportlist {
   171  		s := exportn.Sym
   172  		n := s.Def
   173  		if n == nil {
   174  			continue
   175  		}
   176  		if n.Op != ONAME {
   177  			continue
   178  		}
   179  		if !exportname(s.Name) {
   180  			continue
   181  		}
   182  		if s.Pkg.Name != "main" {
   183  			continue
   184  		}
   185  		if n.Type.Etype == TFUNC && n.Class == PFUNC {
   186  			// function
   187  			ptabs = append(ptabs, ptabEntry{s: s, t: s.Def.Type})
   188  		} else {
   189  			// variable
   190  			ptabs = append(ptabs, ptabEntry{s: s, t: typPtr(s.Def.Type)})
   191  		}
   192  	}
   193  }
   194  
   195  func dumpglobls() {
   196  	// add globals
   197  	for _, n := range externdcl {
   198  		if n.Op != ONAME {
   199  			continue
   200  		}
   201  
   202  		if n.Type == nil {
   203  			Fatalf("external %v nil type\n", n)
   204  		}
   205  		if n.Class == PFUNC {
   206  			continue
   207  		}
   208  		if n.Sym.Pkg != localpkg {
   209  			continue
   210  		}
   211  		dowidth(n.Type)
   212  		ggloblnod(n)
   213  	}
   214  
   215  	for _, n := range funcsyms {
   216  		dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname, 0)
   217  		ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA)
   218  	}
   219  
   220  	// Do not reprocess funcsyms on next dumpglobls call.
   221  	funcsyms = nil
   222  }
   223  
   224  func Linksym(s *Sym) *obj.LSym {
   225  	if s == nil {
   226  		return nil
   227  	}
   228  	if s.Lsym != nil {
   229  		return s.Lsym
   230  	}
   231  	var name string
   232  	if isblanksym(s) {
   233  		name = "_"
   234  	} else if s.Linkname != "" {
   235  		name = s.Linkname
   236  	} else {
   237  		name = s.Pkg.Prefix + "." + s.Name
   238  	}
   239  
   240  	ls := obj.Linklookup(Ctxt, name, 0)
   241  	s.Lsym = ls
   242  	return ls
   243  }
   244  
   245  func duintxx(s *Sym, off int, v uint64, wid int) int {
   246  	return duintxxLSym(Linksym(s), off, v, wid)
   247  }
   248  
   249  func duintxxLSym(s *obj.LSym, off int, v uint64, wid int) int {
   250  	// Update symbol data directly instead of generating a
   251  	// DATA instruction that liblink will have to interpret later.
   252  	// This reduces compilation time and memory usage.
   253  	off = int(Rnd(int64(off), int64(wid)))
   254  
   255  	return int(obj.Setuintxx(Ctxt, s, int64(off), v, int64(wid)))
   256  }
   257  
   258  func duint8(s *Sym, off int, v uint8) int {
   259  	return duintxx(s, off, uint64(v), 1)
   260  }
   261  
   262  func duint16(s *Sym, off int, v uint16) int {
   263  	return duintxx(s, off, uint64(v), 2)
   264  }
   265  
   266  func duint32(s *Sym, off int, v uint32) int {
   267  	return duintxx(s, off, uint64(v), 4)
   268  }
   269  
   270  func duintptr(s *Sym, off int, v uint64) int {
   271  	return duintxx(s, off, v, Widthptr)
   272  }
   273  
   274  func dbvec(s *Sym, off int, bv bvec) int {
   275  	// Runtime reads the bitmaps as byte arrays. Oblige.
   276  	for j := 0; int32(j) < bv.n; j += 8 {
   277  		word := bv.b[j/32]
   278  		off = duint8(s, off, uint8(word>>(uint(j)%32)))
   279  	}
   280  	return off
   281  }
   282  
   283  func stringsym(s string) (data *obj.LSym) {
   284  	var symname string
   285  	if len(s) > 100 {
   286  		// Huge strings are hashed to avoid long names in object files.
   287  		// Indulge in some paranoia by writing the length of s, too,
   288  		// as protection against length extension attacks.
   289  		h := sha256.New()
   290  		io.WriteString(h, s)
   291  		symname = fmt.Sprintf(".gostring.%d.%x", len(s), h.Sum(nil))
   292  	} else {
   293  		// Small strings get named directly by their contents.
   294  		symname = strconv.Quote(s)
   295  	}
   296  
   297  	const prefix = "go.string."
   298  	symdataname := prefix + symname
   299  
   300  	symdata := obj.Linklookup(Ctxt, symdataname, 0)
   301  
   302  	if !symdata.SeenGlobl() {
   303  		// string data
   304  		off := dsnameLSym(symdata, 0, s)
   305  		ggloblLSym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
   306  	}
   307  
   308  	return symdata
   309  }
   310  
   311  var slicebytes_gen int
   312  
   313  func slicebytes(nam *Node, s string, len int) {
   314  	slicebytes_gen++
   315  	symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen)
   316  	sym := Pkglookup(symname, localpkg)
   317  	sym.Def = newname(sym)
   318  
   319  	off := dsname(sym, 0, s)
   320  	ggloblsym(sym, int32(off), obj.NOPTR|obj.LOCAL)
   321  
   322  	if nam.Op != ONAME {
   323  		Fatalf("slicebytes %v", nam)
   324  	}
   325  	off = int(nam.Xoffset)
   326  	off = dsymptr(nam.Sym, off, sym, 0)
   327  	off = duintxx(nam.Sym, off, uint64(len), Widthint)
   328  	duintxx(nam.Sym, off, uint64(len), Widthint)
   329  }
   330  
   331  func dsname(s *Sym, off int, t string) int {
   332  	return dsnameLSym(Linksym(s), off, t)
   333  }
   334  
   335  func dsnameLSym(s *obj.LSym, off int, t string) int {
   336  	s.WriteString(Ctxt, int64(off), len(t), t)
   337  	return off + len(t)
   338  }
   339  
   340  func dsymptr(s *Sym, off int, x *Sym, xoff int) int {
   341  	return dsymptrLSym(Linksym(s), off, Linksym(x), xoff)
   342  }
   343  
   344  func dsymptrLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
   345  	off = int(Rnd(int64(off), int64(Widthptr)))
   346  	s.WriteAddr(Ctxt, int64(off), Widthptr, x, int64(xoff))
   347  	off += Widthptr
   348  	return off
   349  }
   350  
   351  func dsymptrOffLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
   352  	s.WriteOff(Ctxt, int64(off), x, int64(xoff))
   353  	off += 4
   354  	return off
   355  }
   356  
   357  func dsymptrWeakOffLSym(s *obj.LSym, off int, x *obj.LSym) int {
   358  	s.WriteWeakOff(Ctxt, int64(off), x, 0)
   359  	off += 4
   360  	return off
   361  }
   362  
   363  func gdata(nam *Node, nr *Node, wid int) {
   364  	if nam.Op != ONAME {
   365  		Fatalf("gdata nam op %v", nam.Op)
   366  	}
   367  	if nam.Sym == nil {
   368  		Fatalf("gdata nil nam sym")
   369  	}
   370  	s := Linksym(nam.Sym)
   371  
   372  	switch nr.Op {
   373  	case OLITERAL:
   374  		switch u := nr.Val().U.(type) {
   375  		case bool:
   376  			i := int64(obj.Bool2int(u))
   377  			s.WriteInt(Ctxt, nam.Xoffset, wid, i)
   378  
   379  		case *Mpint:
   380  			s.WriteInt(Ctxt, nam.Xoffset, wid, u.Int64())
   381  
   382  		case *Mpflt:
   383  			f := u.Float64()
   384  			switch nam.Type.Etype {
   385  			case TFLOAT32:
   386  				s.WriteFloat32(Ctxt, nam.Xoffset, float32(f))
   387  			case TFLOAT64:
   388  				s.WriteFloat64(Ctxt, nam.Xoffset, f)
   389  			}
   390  
   391  		case *Mpcplx:
   392  			r := u.Real.Float64()
   393  			i := u.Imag.Float64()
   394  			switch nam.Type.Etype {
   395  			case TCOMPLEX64:
   396  				s.WriteFloat32(Ctxt, nam.Xoffset, float32(r))
   397  				s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i))
   398  			case TCOMPLEX128:
   399  				s.WriteFloat64(Ctxt, nam.Xoffset, r)
   400  				s.WriteFloat64(Ctxt, nam.Xoffset+8, i)
   401  			}
   402  
   403  		case string:
   404  			symdata := stringsym(u)
   405  			s.WriteAddr(Ctxt, nam.Xoffset, Widthptr, symdata, 0)
   406  			s.WriteInt(Ctxt, nam.Xoffset+int64(Widthptr), Widthint, int64(len(u)))
   407  
   408  		default:
   409  			Fatalf("gdata unhandled OLITERAL %v", nr)
   410  		}
   411  
   412  	case OADDR:
   413  		if nr.Left.Op != ONAME {
   414  			Fatalf("gdata ADDR left op %v", nr.Left.Op)
   415  		}
   416  		to := nr.Left
   417  		s.WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(to.Sym), to.Xoffset)
   418  
   419  	case ONAME:
   420  		if nr.Class != PFUNC {
   421  			Fatalf("gdata NAME not PFUNC %d", nr.Class)
   422  		}
   423  		s.WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(funcsym(nr.Sym)), nr.Xoffset)
   424  
   425  	default:
   426  		Fatalf("gdata unhandled op %v %v\n", nr, nr.Op)
   427  	}
   428  }