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