github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/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  	"encoding/json"
     9  	"fmt"
    10  	"strings"
    11  
    12  	"github.com/go-asm/go/cmd/archive"
    13  	"github.com/go-asm/go/cmd/bio"
    14  	"github.com/go-asm/go/cmd/compile/base"
    15  	"github.com/go-asm/go/cmd/compile/ir"
    16  	"github.com/go-asm/go/cmd/compile/noder"
    17  	"github.com/go-asm/go/cmd/compile/objw"
    18  	"github.com/go-asm/go/cmd/compile/pkginit"
    19  	"github.com/go-asm/go/cmd/compile/reflectdata"
    20  	"github.com/go-asm/go/cmd/compile/staticdata"
    21  	"github.com/go-asm/go/cmd/compile/typecheck"
    22  	"github.com/go-asm/go/cmd/compile/types"
    23  	"github.com/go-asm/go/cmd/obj"
    24  	"github.com/go-asm/go/cmd/objabi"
    25  )
    26  
    27  // These modes say which kind of object file to generate.
    28  // The default use of the toolchain is to set both bits,
    29  // generating a combined compiler+linker object, one that
    30  // serves to describe the package to both the compiler and the linker.
    31  // In fact the compiler and linker read nearly disjoint sections of
    32  // that file, though, so in a distributed build setting it can be more
    33  // efficient to split the output into two files, supplying the compiler
    34  // object only to future compilations and the linker object only to
    35  // future links.
    36  //
    37  // By default a combined object is written, but if -linkobj is specified
    38  // on the command line then the default -o output is a compiler object
    39  // and the -linkobj output is a linker object.
    40  const (
    41  	modeCompilerObj = 1 << iota
    42  	modeLinkerObj
    43  )
    44  
    45  func dumpobj() {
    46  	if base.Flag.LinkObj == "" {
    47  		dumpobj1(base.Flag.LowerO, modeCompilerObj|modeLinkerObj)
    48  		return
    49  	}
    50  	dumpobj1(base.Flag.LowerO, modeCompilerObj)
    51  	dumpobj1(base.Flag.LinkObj, modeLinkerObj)
    52  }
    53  
    54  func dumpobj1(outfile string, mode int) {
    55  	bout, err := bio.Create(outfile)
    56  	if err != nil {
    57  		base.FlushErrors()
    58  		fmt.Printf("can't create %s: %v\n", outfile, err)
    59  		base.ErrorExit()
    60  	}
    61  	defer bout.Close()
    62  	bout.WriteString("!<arch>\n")
    63  
    64  	if mode&modeCompilerObj != 0 {
    65  		start := startArchiveEntry(bout)
    66  		dumpCompilerObj(bout)
    67  		finishArchiveEntry(bout, start, "__.PKGDEF")
    68  	}
    69  	if mode&modeLinkerObj != 0 {
    70  		start := startArchiveEntry(bout)
    71  		dumpLinkerObj(bout)
    72  		finishArchiveEntry(bout, start, "_go_.o")
    73  	}
    74  }
    75  
    76  func printObjHeader(bout *bio.Writer) {
    77  	bout.WriteString(objabi.HeaderString())
    78  	if base.Flag.BuildID != "" {
    79  		fmt.Fprintf(bout, "build id %q\n", base.Flag.BuildID)
    80  	}
    81  	if types.LocalPkg.Name == "main" {
    82  		fmt.Fprintf(bout, "main\n")
    83  	}
    84  	fmt.Fprintf(bout, "\n") // header ends with blank line
    85  }
    86  
    87  func startArchiveEntry(bout *bio.Writer) int64 {
    88  	var arhdr [archive.HeaderSize]byte
    89  	bout.Write(arhdr[:])
    90  	return bout.Offset()
    91  }
    92  
    93  func finishArchiveEntry(bout *bio.Writer, start int64, name string) {
    94  	bout.Flush()
    95  	size := bout.Offset() - start
    96  	if size&1 != 0 {
    97  		bout.WriteByte(0)
    98  	}
    99  	bout.MustSeek(start-archive.HeaderSize, 0)
   100  
   101  	var arhdr [archive.HeaderSize]byte
   102  	archive.FormatHeader(arhdr[:], name, size)
   103  	bout.Write(arhdr[:])
   104  	bout.Flush()
   105  	bout.MustSeek(start+size+(size&1), 0)
   106  }
   107  
   108  func dumpCompilerObj(bout *bio.Writer) {
   109  	printObjHeader(bout)
   110  	noder.WriteExports(bout)
   111  }
   112  
   113  func dumpdata() {
   114  	reflectdata.WriteGCSymbols()
   115  	reflectdata.WritePluginTable()
   116  	dumpembeds()
   117  
   118  	if reflectdata.ZeroSize > 0 {
   119  		zero := base.PkgLinksym("go:map", "zero", obj.ABI0)
   120  		objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA)
   121  		zero.Set(obj.AttrStatic, true)
   122  	}
   123  
   124  	staticdata.WriteFuncSyms()
   125  	addGCLocals()
   126  }
   127  
   128  func dumpLinkerObj(bout *bio.Writer) {
   129  	printObjHeader(bout)
   130  
   131  	if len(typecheck.Target.CgoPragmas) != 0 {
   132  		// write empty export section; must be before cgo section
   133  		fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
   134  		fmt.Fprintf(bout, "\n$$  // cgo\n")
   135  		if err := json.NewEncoder(bout).Encode(typecheck.Target.CgoPragmas); err != nil {
   136  			base.Fatalf("serializing pragcgobuf: %v", err)
   137  		}
   138  		fmt.Fprintf(bout, "\n$$\n\n")
   139  	}
   140  
   141  	fmt.Fprintf(bout, "\n!\n")
   142  
   143  	obj.WriteObjFile(base.Ctxt, bout)
   144  }
   145  
   146  func dumpGlobal(n *ir.Name) {
   147  	if n.Type() == nil {
   148  		base.Fatalf("external %v nil type\n", n)
   149  	}
   150  	if n.Class == ir.PFUNC {
   151  		return
   152  	}
   153  	if n.Sym().Pkg != types.LocalPkg {
   154  		return
   155  	}
   156  	types.CalcSize(n.Type())
   157  	ggloblnod(n)
   158  	if n.CoverageCounter() || n.CoverageAuxVar() || n.Linksym().Static() {
   159  		return
   160  	}
   161  	base.Ctxt.DwarfGlobal(types.TypeSymName(n.Type()), n.Linksym())
   162  }
   163  
   164  func dumpGlobalConst(n *ir.Name) {
   165  	// only export typed constants
   166  	t := n.Type()
   167  	if t == nil {
   168  		return
   169  	}
   170  	if n.Sym().Pkg != types.LocalPkg {
   171  		return
   172  	}
   173  	// only export integer constants for now
   174  	if !t.IsInteger() {
   175  		return
   176  	}
   177  	v := n.Val()
   178  	if t.IsUntyped() {
   179  		// Export untyped integers as int (if they fit).
   180  		t = types.Types[types.TINT]
   181  		if ir.ConstOverflow(v, t) {
   182  			return
   183  		}
   184  	} else {
   185  		// If the type of the constant is an instantiated generic, we need to emit
   186  		// that type so the linker knows about it. See issue 51245.
   187  		_ = reflectdata.TypeLinksym(t)
   188  	}
   189  	base.Ctxt.DwarfIntConst(n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v))
   190  }
   191  
   192  // addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data.
   193  //
   194  // This is done during the sequential phase after compilation, since
   195  // global symbols can't be declared during parallel compilation.
   196  func addGCLocals() {
   197  	for _, s := range base.Ctxt.Text {
   198  		fn := s.Func()
   199  		if fn == nil {
   200  			continue
   201  		}
   202  		for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} {
   203  			if gcsym != nil && !gcsym.OnList() {
   204  				objw.Global(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK)
   205  			}
   206  		}
   207  		if x := fn.StackObjects; x != nil {
   208  			objw.Global(x, int32(len(x.P)), obj.RODATA)
   209  			x.Set(obj.AttrStatic, true)
   210  		}
   211  		if x := fn.OpenCodedDeferInfo; x != nil {
   212  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   213  		}
   214  		if x := fn.ArgInfo; x != nil {
   215  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   216  			x.Set(obj.AttrStatic, true)
   217  		}
   218  		if x := fn.ArgLiveInfo; x != nil {
   219  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   220  			x.Set(obj.AttrStatic, true)
   221  		}
   222  		if x := fn.WrapInfo; x != nil && !x.OnList() {
   223  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
   224  			x.Set(obj.AttrStatic, true)
   225  		}
   226  		for _, jt := range fn.JumpTables {
   227  			objw.Global(jt.Sym, int32(len(jt.Targets)*base.Ctxt.Arch.PtrSize), obj.RODATA)
   228  		}
   229  	}
   230  }
   231  
   232  func ggloblnod(nam *ir.Name) {
   233  	s := nam.Linksym()
   234  
   235  	// main_inittask and runtime_inittask in package runtime (and in
   236  	// test/initempty.go) aren't real variable declarations, but
   237  	// linknamed variables pointing to the compiler's generated
   238  	// .inittask symbol. The real symbol was already written out in
   239  	// pkginit.Task, so we need to avoid writing them out a second time
   240  	// here, otherwise base.Ctxt.Globl will fail.
   241  	if strings.HasSuffix(s.Name, "..inittask") && s.OnList() {
   242  		return
   243  	}
   244  
   245  	s.Gotype = reflectdata.TypeLinksym(nam.Type())
   246  	flags := 0
   247  	if nam.Readonly() {
   248  		flags = obj.RODATA
   249  	}
   250  	if nam.Type() != nil && !nam.Type().HasPointers() {
   251  		flags |= obj.NOPTR
   252  	}
   253  	size := nam.Type().Size()
   254  	linkname := nam.Sym().Linkname
   255  	name := nam.Sym().Name
   256  
   257  	// We've skipped linkname'd globals's instrument, so we can skip them here as well.
   258  	if base.Flag.ASan && linkname == "" && pkginit.InstrumentGlobalsMap[name] != nil {
   259  		// Write the new size of instrumented global variables that have
   260  		// trailing redzones into object file.
   261  		rzSize := pkginit.GetRedzoneSizeForGlobal(size)
   262  		sizeWithRZ := rzSize + size
   263  		base.Ctxt.Globl(s, sizeWithRZ, flags)
   264  	} else {
   265  		base.Ctxt.Globl(s, size, flags)
   266  	}
   267  	if nam.Libfuzzer8BitCounter() {
   268  		s.Type = objabi.SLIBFUZZER_8BIT_COUNTER
   269  	}
   270  	if nam.CoverageCounter() {
   271  		s.Type = objabi.SCOVERAGE_COUNTER
   272  	}
   273  	if nam.Sym().Linkname != "" {
   274  		// Make sure linkname'd symbol is non-package. When a symbol is
   275  		// both imported and linkname'd, s.Pkg may not set to "_" in
   276  		// types.Sym.Linksym because LSym already exists. Set it here.
   277  		s.Pkg = "_"
   278  	}
   279  }
   280  
   281  func dumpembeds() {
   282  	for _, v := range typecheck.Target.Embeds {
   283  		staticdata.WriteEmbed(v)
   284  	}
   285  }