| // 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/objw" |
| "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" |
| "go/constant" |
| ) |
| |
| // 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) { |
| fmt.Fprintf(bout, "go object %s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring()) |
| 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) |
| dumpexport(bout) |
| } |
| |
| func dumpdata() { |
| numExterns := len(typecheck.Target.Externs) |
| numDecls := len(typecheck.Target.Decls) |
| |
| dumpglobls(typecheck.Target.Externs) |
| staticdata.WriteFuncSyms() |
| reflectdata.CollectPTabs() |
| numExports := len(typecheck.Target.Exports) |
| addsignats(typecheck.Target.Externs) |
| reflectdata.WriteRuntimeTypes() |
| reflectdata.WriteTabs() |
| numPTabs, numITabs := reflectdata.CountTabs() |
| reflectdata.WriteImportStrings() |
| reflectdata.WriteBasicTypes() |
| dumpembeds() |
| |
| // Calls to dumpsignats can generate functions, |
| // like method wrappers and hash and equality routines. |
| // Compile any generated functions, process any new resulting types, repeat. |
| // This can't loop forever, because there is no way to generate an infinite |
| // number of types in a finite amount of code. |
| // In the typical case, we loop 0 or 1 times. |
| // It was not until issue 24761 that we found any code that required a loop at all. |
| for { |
| for i := numDecls; i < len(typecheck.Target.Decls); i++ { |
| n := typecheck.Target.Decls[i] |
| if n.Op() == ir.ODCLFUNC { |
| funccompile(n.(*ir.Func)) |
| } |
| } |
| numDecls = len(typecheck.Target.Decls) |
| compileFunctions() |
| reflectdata.WriteRuntimeTypes() |
| if numDecls == len(typecheck.Target.Decls) { |
| break |
| } |
| } |
| |
| // Dump extra globals. |
| dumpglobls(typecheck.Target.Externs[numExterns:]) |
| |
| if reflectdata.ZeroSize > 0 { |
| zero := ir.Pkgs.Map.Lookup("zero") |
| objw.Global(zero.Linksym(), int32(reflectdata.ZeroSize), obj.DUPOK|obj.RODATA) |
| } |
| |
| addGCLocals() |
| |
| if numExports != len(typecheck.Target.Exports) { |
| base.Fatalf("Target.Exports changed after compile functions loop") |
| } |
| newNumPTabs, newNumITabs := reflectdata.CountTabs() |
| if newNumPTabs != numPTabs { |
| base.Fatalf("ptabs changed after compile functions loop") |
| } |
| if newNumITabs != numITabs { |
| base.Fatalf("itabs changed after compile functions loop") |
| } |
| } |
| |
| 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) |
| } |
| |
| func dumpGlobalConst(n ir.Node) { |
| // 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 |
| } |
| } |
| base.Ctxt.DwarfIntConst(base.Ctxt.Pkgpath, n.Sym().Name, types.TypeSymName(t), ir.IntVal(t, v)) |
| } |
| |
| func dumpglobls(externs []ir.Node) { |
| // add globals |
| for _, n := range externs { |
| switch n.Op() { |
| case ir.ONAME: |
| dumpGlobal(n.(*ir.Name)) |
| case ir.OLITERAL: |
| dumpGlobalConst(n) |
| } |
| } |
| } |
| |
| // 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 { |
| attr := int16(obj.RODATA) |
| objw.Global(x, int32(len(x.P)), attr) |
| x.Set(obj.AttrStatic, true) |
| } |
| if x := fn.OpenCodedDeferInfo; x != nil { |
| objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK) |
| } |
| } |
| } |
| |
| // litsym writes the static literal c to n. |
| // Neither n nor c is modified. |
| func litsym(n *ir.Name, noff int64, c ir.Node, wid int) { |
| if n.Op() != ir.ONAME { |
| base.Fatalf("litsym n op %v", n.Op()) |
| } |
| if n.Sym() == nil { |
| base.Fatalf("litsym nil n sym") |
| } |
| if c.Op() == ir.ONIL { |
| return |
| } |
| if c.Op() != ir.OLITERAL { |
| base.Fatalf("litsym c op %v", c.Op()) |
| } |
| s := n.Sym().Linksym() |
| switch u := c.Val(); u.Kind() { |
| case constant.Bool: |
| i := int64(obj.Bool2int(constant.BoolVal(u))) |
| s.WriteInt(base.Ctxt, noff, wid, i) |
| |
| case constant.Int: |
| s.WriteInt(base.Ctxt, noff, wid, ir.IntVal(c.Type(), u)) |
| |
| case constant.Float: |
| f, _ := constant.Float64Val(u) |
| switch c.Type().Kind() { |
| case types.TFLOAT32: |
| s.WriteFloat32(base.Ctxt, noff, float32(f)) |
| case types.TFLOAT64: |
| s.WriteFloat64(base.Ctxt, noff, f) |
| } |
| |
| case constant.Complex: |
| re, _ := constant.Float64Val(constant.Real(u)) |
| im, _ := constant.Float64Val(constant.Imag(u)) |
| switch c.Type().Kind() { |
| case types.TCOMPLEX64: |
| s.WriteFloat32(base.Ctxt, noff, float32(re)) |
| s.WriteFloat32(base.Ctxt, noff+4, float32(im)) |
| case types.TCOMPLEX128: |
| s.WriteFloat64(base.Ctxt, noff, re) |
| s.WriteFloat64(base.Ctxt, noff+8, im) |
| } |
| |
| case constant.String: |
| i := constant.StringVal(u) |
| symdata := staticdata.StringSym(n.Pos(), i) |
| s.WriteAddr(base.Ctxt, noff, types.PtrSize, symdata, 0) |
| s.WriteInt(base.Ctxt, noff+int64(types.PtrSize), types.PtrSize, int64(len(i))) |
| |
| default: |
| base.Fatalf("litsym unhandled OLITERAL %v", c) |
| } |
| } |
| |
| func ggloblnod(nam ir.Node) { |
| s := nam.Sym().Linksym() |
| s.Gotype = ngotype(nam).Linksym() |
| flags := 0 |
| if nam.Name().Readonly() { |
| flags = obj.RODATA |
| } |
| if nam.Type() != nil && !nam.Type().HasPointers() { |
| flags |= obj.NOPTR |
| } |
| base.Ctxt.Globl(s, nam.Type().Width, flags) |
| if nam.Name().LibfuzzerExtraCounter() { |
| s.Type = objabi.SLIBFUZZER_EXTRA_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) |
| } |
| } |
| |
| func addsignats(dcls []ir.Node) { |
| // copy types from dcl list to signatset |
| for _, n := range dcls { |
| if n.Op() == ir.OTYPE { |
| reflectdata.NeedRuntimeType(n.Type()) |
| } |
| } |
| } |