diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/obj.go')
-rw-r--r-- | src/cmd/compile/internal/gc/obj.go | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go new file mode 100644 index 0000000..e090caf --- /dev/null +++ b/src/cmd/compile/internal/gc/obj.go @@ -0,0 +1,284 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gc + +import ( + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/noder" + "cmd/compile/internal/objw" + "cmd/compile/internal/pkginit" + "cmd/compile/internal/reflectdata" + "cmd/compile/internal/staticdata" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/archive" + "cmd/internal/bio" + "cmd/internal/obj" + "cmd/internal/objabi" + "encoding/json" + "fmt" + "strings" +) + +// These modes say which kind of object file to generate. +// The default use of the toolchain is to set both bits, +// generating a combined compiler+linker object, one that +// serves to describe the package to both the compiler and the linker. +// In fact the compiler and linker read nearly disjoint sections of +// that file, though, so in a distributed build setting it can be more +// efficient to split the output into two files, supplying the compiler +// object only to future compilations and the linker object only to +// future links. +// +// By default a combined object is written, but if -linkobj is specified +// on the command line then the default -o output is a compiler object +// and the -linkobj output is a linker object. +const ( + modeCompilerObj = 1 << iota + modeLinkerObj +) + +func dumpobj() { + if base.Flag.LinkObj == "" { + dumpobj1(base.Flag.LowerO, modeCompilerObj|modeLinkerObj) + return + } + dumpobj1(base.Flag.LowerO, modeCompilerObj) + dumpobj1(base.Flag.LinkObj, modeLinkerObj) +} + +func dumpobj1(outfile string, mode int) { + bout, err := bio.Create(outfile) + if err != nil { + base.FlushErrors() + fmt.Printf("can't create %s: %v\n", outfile, err) + base.ErrorExit() + } + defer bout.Close() + bout.WriteString("!<arch>\n") + + if mode&modeCompilerObj != 0 { + start := startArchiveEntry(bout) + dumpCompilerObj(bout) + finishArchiveEntry(bout, start, "__.PKGDEF") + } + if mode&modeLinkerObj != 0 { + start := startArchiveEntry(bout) + dumpLinkerObj(bout) + finishArchiveEntry(bout, start, "_go_.o") + } +} + +func printObjHeader(bout *bio.Writer) { + bout.WriteString(objabi.HeaderString()) + if base.Flag.BuildID != "" { + fmt.Fprintf(bout, "build id %q\n", base.Flag.BuildID) + } + if types.LocalPkg.Name == "main" { + fmt.Fprintf(bout, "main\n") + } + fmt.Fprintf(bout, "\n") // header ends with blank line +} + +func startArchiveEntry(bout *bio.Writer) int64 { + var arhdr [archive.HeaderSize]byte + bout.Write(arhdr[:]) + return bout.Offset() +} + +func finishArchiveEntry(bout *bio.Writer, start int64, name string) { + bout.Flush() + size := bout.Offset() - start + if size&1 != 0 { + bout.WriteByte(0) + } + bout.MustSeek(start-archive.HeaderSize, 0) + + var arhdr [archive.HeaderSize]byte + archive.FormatHeader(arhdr[:], name, size) + bout.Write(arhdr[:]) + bout.Flush() + bout.MustSeek(start+size+(size&1), 0) +} + +func dumpCompilerObj(bout *bio.Writer) { + printObjHeader(bout) + noder.WriteExports(bout) +} + +func dumpdata() { + reflectdata.WriteGCSymbols() + reflectdata.WritePluginTable() + dumpembeds() + + if reflectdata.ZeroSize > 0 { + zero := base.PkgLinksym("go:map", "zero", obj.ABI0) + objw.Global(zero, int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA) + zero.Set(obj.AttrStatic, true) + } + + staticdata.WriteFuncSyms() + addGCLocals() +} + +func dumpLinkerObj(bout *bio.Writer) { + printObjHeader(bout) + + if len(typecheck.Target.CgoPragmas) != 0 { + // write empty export section; must be before cgo section + fmt.Fprintf(bout, "\n$$\n\n$$\n\n") + fmt.Fprintf(bout, "\n$$ // cgo\n") + if err := json.NewEncoder(bout).Encode(typecheck.Target.CgoPragmas); err != nil { + base.Fatalf("serializing pragcgobuf: %v", err) + } + fmt.Fprintf(bout, "\n$$\n\n") + } + + fmt.Fprintf(bout, "\n!\n") + + obj.WriteObjFile(base.Ctxt, bout) +} + +func dumpGlobal(n *ir.Name) { + if n.Type() == nil { + base.Fatalf("external %v nil type\n", n) + } + if n.Class == ir.PFUNC { + return + } + if n.Sym().Pkg != types.LocalPkg { + return + } + types.CalcSize(n.Type()) + ggloblnod(n) + if n.CoverageCounter() || n.CoverageAuxVar() || n.Linksym().Static() { + return + } + base.Ctxt.DwarfGlobal(types.TypeSymName(n.Type()), n.Linksym()) +} + +func dumpGlobalConst(n *ir.Name) { + // only export typed constants + t := n.Type() + if t == nil { + return + } + if n.Sym().Pkg != types.LocalPkg { + return + } + // only export integer constants for now + if !t.IsInteger() { + return + } + v := n.Val() + if t.IsUntyped() { + // Export untyped integers as int (if they fit). + t = types.Types[types.TINT] + if ir.ConstOverflow(v, t) { + return + } + } else { + // If the type of the constant is an instantiated generic, we need to emit + // that type so the linker knows about it. See issue 51245. + _ = reflectdata.TypeLinksym(t) + } + base.Ctxt.DwarfIntConst(n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v)) +} + +// addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data. +// +// This is done during the sequential phase after compilation, since +// global symbols can't be declared during parallel compilation. +func addGCLocals() { + for _, s := range base.Ctxt.Text { + fn := s.Func() + if fn == nil { + continue + } + for _, gcsym := range []*obj.LSym{fn.GCArgs, fn.GCLocals} { + if gcsym != nil && !gcsym.OnList() { + objw.Global(gcsym, int32(len(gcsym.P)), obj.RODATA|obj.DUPOK) + } + } + if x := fn.StackObjects; x != nil { + objw.Global(x, int32(len(x.P)), obj.RODATA) + x.Set(obj.AttrStatic, true) + } + if x := fn.OpenCodedDeferInfo; x != nil { + objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) + } + if x := fn.ArgInfo; x != nil { + objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) + x.Set(obj.AttrStatic, true) + } + if x := fn.ArgLiveInfo; x != nil { + objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) + x.Set(obj.AttrStatic, true) + } + if x := fn.WrapInfo; x != nil && !x.OnList() { + objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) + x.Set(obj.AttrStatic, true) + } + for _, jt := range fn.JumpTables { + objw.Global(jt.Sym, int32(len(jt.Targets)*base.Ctxt.Arch.PtrSize), obj.RODATA) + } + } +} + +func ggloblnod(nam *ir.Name) { + s := nam.Linksym() + + // main_inittask and runtime_inittask in package runtime (and in + // test/initempty.go) aren't real variable declarations, but + // linknamed variables pointing to the compiler's generated + // .inittask symbol. The real symbol was already written out in + // pkginit.Task, so we need to avoid writing them out a second time + // here, otherwise base.Ctxt.Globl will fail. + if strings.HasSuffix(s.Name, "..inittask") && s.OnList() { + return + } + + s.Gotype = reflectdata.TypeLinksym(nam.Type()) + flags := 0 + if nam.Readonly() { + flags = obj.RODATA + } + if nam.Type() != nil && !nam.Type().HasPointers() { + flags |= obj.NOPTR + } + size := nam.Type().Size() + linkname := nam.Sym().Linkname + name := nam.Sym().Name + + // We've skipped linkname'd globals's instrument, so we can skip them here as well. + if base.Flag.ASan && linkname == "" && pkginit.InstrumentGlobalsMap[name] != nil { + // Write the new size of instrumented global variables that have + // trailing redzones into object file. + rzSize := pkginit.GetRedzoneSizeForGlobal(size) + sizeWithRZ := rzSize + size + base.Ctxt.Globl(s, sizeWithRZ, flags) + } else { + base.Ctxt.Globl(s, size, flags) + } + if nam.Libfuzzer8BitCounter() { + s.Type = objabi.SLIBFUZZER_8BIT_COUNTER + } + if nam.CoverageCounter() { + s.Type = objabi.SCOVERAGE_COUNTER + } + if nam.Sym().Linkname != "" { + // Make sure linkname'd symbol is non-package. When a symbol is + // both imported and linkname'd, s.Pkg may not set to "_" in + // types.Sym.Linksym because LSym already exists. Set it here. + s.Pkg = "_" + } +} + +func dumpembeds() { + for _, v := range typecheck.Target.Embeds { + staticdata.WriteEmbed(v) + } +} |