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