Source file src/cmd/compile/internal/gc/obj.go

     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/compile/internal/base"
     9  	"cmd/compile/internal/ir"
    10  	"cmd/compile/internal/noder"
    11  	"cmd/compile/internal/objw"
    12  	"cmd/compile/internal/reflectdata"
    13  	"cmd/compile/internal/staticdata"
    14  	"cmd/compile/internal/typecheck"
    15  	"cmd/compile/internal/types"
    16  	"cmd/internal/archive"
    17  	"cmd/internal/bio"
    18  	"cmd/internal/obj"
    19  	"cmd/internal/objabi"
    20  	"encoding/json"
    21  	"fmt"
    22  )
    23  
    24  // These modes say which kind of object file to generate.
    25  // The default use of the toolchain is to set both bits,
    26  // generating a combined compiler+linker object, one that
    27  // serves to describe the package to both the compiler and the linker.
    28  // In fact the compiler and linker read nearly disjoint sections of
    29  // that file, though, so in a distributed build setting it can be more
    30  // efficient to split the output into two files, supplying the compiler
    31  // object only to future compilations and the linker object only to
    32  // future links.
    33  //
    34  // By default a combined object is written, but if -linkobj is specified
    35  // on the command line then the default -o output is a compiler object
    36  // and the -linkobj output is a linker object.
    37  const (
    38  	modeCompilerObj = 1 << iota
    39  	modeLinkerObj
    40  )
    41  
    42  func dumpobj() {
    43  	if base.Flag.LinkObj == "" {
    44  		dumpobj1(base.Flag.LowerO, modeCompilerObj|modeLinkerObj)
    45  		return
    46  	}
    47  	dumpobj1(base.Flag.LowerO, modeCompilerObj)
    48  	dumpobj1(base.Flag.LinkObj, modeLinkerObj)
    49  }
    50  
    51  func dumpobj1(outfile string, mode int) {
    52  	bout, err := bio.Create(outfile)
    53  	if err != nil {
    54  		base.FlushErrors()
    55  		fmt.Printf("can't create %s: %v\n", outfile, err)
    56  		base.ErrorExit()
    57  	}
    58  	defer bout.Close()
    59  	bout.WriteString("!<arch>\n")
    60  
    61  	if mode&modeCompilerObj != 0 {
    62  		start := startArchiveEntry(bout)
    63  		dumpCompilerObj(bout)
    64  		finishArchiveEntry(bout, start, "__.PKGDEF")
    65  	}
    66  	if mode&modeLinkerObj != 0 {
    67  		start := startArchiveEntry(bout)
    68  		dumpLinkerObj(bout)
    69  		finishArchiveEntry(bout, start, "_go_.o")
    70  	}
    71  }
    72  
    73  func printObjHeader(bout *bio.Writer) {
    74  	bout.WriteString(objabi.HeaderString())
    75  	if base.Flag.BuildID != "" {
    76  		fmt.Fprintf(bout, "build id %q\n", base.Flag.BuildID)
    77  	}
    78  	if types.LocalPkg.Name == "main" {
    79  		fmt.Fprintf(bout, "main\n")
    80  	}
    81  	fmt.Fprintf(bout, "\n") // header ends with blank line
    82  }
    83  
    84  func startArchiveEntry(bout *bio.Writer) int64 {
    85  	var arhdr [archive.HeaderSize]byte
    86  	bout.Write(arhdr[:])
    87  	return bout.Offset()
    88  }
    89  
    90  func finishArchiveEntry(bout *bio.Writer, start int64, name string) {
    91  	bout.Flush()
    92  	size := bout.Offset() - start
    93  	if size&1 != 0 {
    94  		bout.WriteByte(0)
    95  	}
    96  	bout.MustSeek(start-archive.HeaderSize, 0)
    97  
    98  	var arhdr [archive.HeaderSize]byte
    99  	archive.FormatHeader(arhdr[:], name, size)
   100  	bout.Write(arhdr[:])
   101  	bout.Flush()
   102  	bout.MustSeek(start+size+(size&1), 0)
   103  }
   104  
   105  func dumpCompilerObj(bout *bio.Writer) {
   106  	printObjHeader(bout)
   107  	noder.WriteExports(bout)
   108  }
   109  
   110  func dumpdata() {
   111  	numExterns := len(typecheck.Target.Externs)
   112  	numDecls := len(typecheck.Target.Decls)
   113  
   114  	dumpglobls(typecheck.Target.Externs)
   115  	reflectdata.CollectPTabs()
   116  	numExports := len(typecheck.Target.Exports)
   117  	addsignats(typecheck.Target.Externs)
   118  	reflectdata.WriteRuntimeTypes()
   119  	reflectdata.WriteTabs()
   120  	numPTabs := reflectdata.CountPTabs()
   121  	reflectdata.WriteImportStrings()
   122  	reflectdata.WriteBasicTypes()
   123  	dumpembeds()
   124  
   125  	// Calls to WriteRuntimeTypes can generate functions,
   126  	// like method wrappers and hash and equality routines.
   127  	// Compile any generated functions, process any new resulting types, repeat.
   128  	// This can't loop forever, because there is no way to generate an infinite
   129  	// number of types in a finite amount of code.
   130  	// In the typical case, we loop 0 or 1 times.
   131  	// It was not until issue 24761 that we found any code that required a loop at all.
   132  	for {
   133  		for i := numDecls; i < len(typecheck.Target.Decls); i++ {
   134  			if n, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
   135  				enqueueFunc(n)
   136  			}
   137  		}
   138  		numDecls = len(typecheck.Target.Decls)
   139  		compileFunctions()
   140  		reflectdata.WriteRuntimeTypes()
   141  		if numDecls == len(typecheck.Target.Decls) {
   142  			break
   143  		}
   144  	}
   145  
   146  	// Dump extra globals.
   147  	dumpglobls(typecheck.Target.Externs[numExterns:])
   148  
   149  	if reflectdata.ZeroSize > 0 {
   150  		zero := base.PkgLinksym("go.map", "zero", obj.ABI0)
   151  		objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA)
   152  		zero.Set(obj.AttrStatic, true)
   153  	}
   154  
   155  	staticdata.WriteFuncSyms()
   156  	addGCLocals()
   157  
   158  	if numExports != len(typecheck.Target.Exports) {
   159  		base.Fatalf("Target.Exports changed after compile functions loop")
   160  	}
   161  	newNumPTabs := reflectdata.CountPTabs()
   162  	if newNumPTabs != numPTabs {
   163  		base.Fatalf("ptabs changed after compile functions loop")
   164  	}
   165  }
   166  
   167  func dumpLinkerObj(bout *bio.Writer) {
   168  	printObjHeader(bout)
   169  
   170  	if len(typecheck.Target.CgoPragmas) != 0 {
   171  		// write empty export section; must be before cgo section
   172  		fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
   173  		fmt.Fprintf(bout, "\n$$  // cgo\n")
   174  		if err := json.NewEncoder(bout).Encode(typecheck.Target.CgoPragmas); err != nil {
   175  			base.Fatalf("serializing pragcgobuf: %v", err)
   176  		}
   177  		fmt.Fprintf(bout, "\n$$\n\n")
   178  	}
   179  
   180  	fmt.Fprintf(bout, "\n!\n")
   181  
   182  	obj.WriteObjFile(base.Ctxt, bout)
   183  }
   184  
   185  func dumpGlobal(n *ir.Name) {
   186  	if n.Type() == nil {
   187  		base.Fatalf("external %v nil type\n", n)
   188  	}
   189  	if n.Class == ir.PFUNC {
   190  		return
   191  	}
   192  	if n.Sym().Pkg != types.LocalPkg {
   193  		return
   194  	}
   195  	types.CalcSize(n.Type())
   196  	ggloblnod(n)
   197  	base.Ctxt.DwarfGlobal(base.Ctxt.Pkgpath, types.TypeSymName(n.Type()), n.Linksym())
   198  }
   199  
   200  func dumpGlobalConst(n ir.Node) {
   201  	// only export typed constants
   202  	t := n.Type()
   203  	if t == nil {
   204  		return
   205  	}
   206  	if n.Sym().Pkg != types.LocalPkg {
   207  		return
   208  	}
   209  	// only export integer constants for now
   210  	if !t.IsInteger() {
   211  		return
   212  	}
   213  	v := n.Val()
   214  	if t.IsUntyped() {
   215  		// Export untyped integers as int (if they fit).
   216  		t = types.Types[types.TINT]
   217  		if ir.ConstOverflow(v, t) {
   218  			return
   219  		}
   220  	} else {
   221  		// If the type of the constant is an instantiated generic, we need to emit
   222  		// that type so the linker knows about it. See issue 51245.
   223  		_ = reflectdata.TypeLinksym(t)
   224  	}
   225  	base.Ctxt.DwarfIntConst(base.Ctxt.Pkgpath, n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v))
   226  }
   227  
   228  func dumpglobls(externs []ir.Node) {
   229  	// add globals
   230  	for _, n := range externs {
   231  		switch n.Op() {
   232  		case ir.ONAME:
   233  			dumpGlobal(n.(*ir.Name))
   234  		case ir.OLITERAL:
   235  			dumpGlobalConst(n)
   236  		}
   237  	}
   238  }
   239  
   240  // addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data.
   241  //
   242  // This is done during the sequential phase after compilation, since
   243  // global symbols can't be declared during parallel compilation.
   244  func addGCLocals() {
   245  	for _, s := range base.Ctxt.Text {
   246  		fn := s.Func()
   247  		if fn == nil {
   248  			continue
   249  		}
   250  		for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} {
   251  			if gcsym != nil && !gcsym.OnList() {
   252  				objw.Global(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
   253  			}
   254  		}
   255  		if x := fn.StackObjects; x != nil {
   256  			objw.Global(x, int32(len(x.P)), obj.RODATA)
   257  			x.Set(obj.AttrStatic, true)
   258  		}
   259  		if x := fn.OpenCodedDeferInfo; x != nil {
   260  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   261  		}
   262  		if x := fn.ArgInfo; x != nil {
   263  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   264  			x.Set(obj.AttrStatic, true)
   265  		}
   266  		if x := fn.ArgLiveInfo; x != nil {
   267  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   268  			x.Set(obj.AttrStatic, true)
   269  		}
   270  		if x := fn.WrapInfo; x != nil && !x.OnList() {
   271  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   272  			x.Set(obj.AttrStatic, true)
   273  		}
   274  	}
   275  }
   276  
   277  func ggloblnod(nam *ir.Name) {
   278  	s := nam.Linksym()
   279  	s.Gotype = reflectdata.TypeLinksym(nam.Type())
   280  	flags := 0
   281  	if nam.Readonly() {
   282  		flags = obj.RODATA
   283  	}
   284  	if nam.Type() != nil && !nam.Type().HasPointers() {
   285  		flags |= obj.NOPTR
   286  	}
   287  	base.Ctxt.Globl(s, nam.Type().Size(), flags)
   288  	if nam.LibfuzzerExtraCounter() {
   289  		s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
   290  	}
   291  	if nam.Sym().Linkname != "" {
   292  		// Make sure linkname'd symbol is non-package. When a symbol is
   293  		// both imported and linkname'd, s.Pkg may not set to "_" in
   294  		// types.Sym.Linksym because LSym already exists. Set it here.
   295  		s.Pkg = "_"
   296  	}
   297  }
   298  
   299  func dumpembeds() {
   300  	for _, v := range typecheck.Target.Embeds {
   301  		staticdata.WriteEmbed(v)
   302  	}
   303  }
   304  
   305  func addsignats(dcls []ir.Node) {
   306  	// copy types from dcl list to signatset
   307  	for _, n := range dcls {
   308  		if n.Op() == ir.OTYPE {
   309  			reflectdata.NeedRuntimeType(n.Type())
   310  		}
   311  	}
   312  }
   313  

View as plain text