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 staticdata
6
7import (
8	"crypto/sha256"
9	"fmt"
10	"go/constant"
11	"internal/buildcfg"
12	"io"
13	"io/ioutil"
14	"os"
15	"sort"
16	"strconv"
17	"sync"
18
19	"cmd/compile/internal/base"
20	"cmd/compile/internal/ir"
21	"cmd/compile/internal/objw"
22	"cmd/compile/internal/typecheck"
23	"cmd/compile/internal/types"
24	"cmd/internal/obj"
25	"cmd/internal/objabi"
26	"cmd/internal/src"
27)
28
29// InitAddrOffset writes the static name symbol lsym to n, it does not modify n.
30// It's the caller responsibility to make sure lsym is from ONAME/PEXTERN node.
31func InitAddrOffset(n *ir.Name, noff int64, lsym *obj.LSym, off int64) {
32	if n.Op() != ir.ONAME {
33		base.Fatalf("InitAddr n op %v", n.Op())
34	}
35	if n.Sym() == nil {
36		base.Fatalf("InitAddr nil n sym")
37	}
38	s := n.Linksym()
39	s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, off)
40}
41
42// InitAddr is InitAddrOffset, with offset fixed to 0.
43func InitAddr(n *ir.Name, noff int64, lsym *obj.LSym) {
44	InitAddrOffset(n, noff, lsym, 0)
45}
46
47// InitSlice writes a static slice symbol {lsym, lencap, lencap} to n+noff, it does not modify n.
48// It's the caller responsibility to make sure lsym is from ONAME node.
49func InitSlice(n *ir.Name, noff int64, lsym *obj.LSym, lencap int64) {
50	s := n.Linksym()
51	s.WriteAddr(base.Ctxt, noff, types.PtrSize, lsym, 0)
52	s.WriteInt(base.Ctxt, noff+types.SliceLenOffset, types.PtrSize, lencap)
53	s.WriteInt(base.Ctxt, noff+types.SliceCapOffset, types.PtrSize, lencap)
54}
55
56func InitSliceBytes(nam *ir.Name, off int64, s string) {
57	if nam.Op() != ir.ONAME {
58		base.Fatalf("InitSliceBytes %v", nam)
59	}
60	InitSlice(nam, off, slicedata(nam.Pos(), s).Linksym(), int64(len(s)))
61}
62
63const (
64	stringSymPrefix  = "go.string."
65	stringSymPattern = ".gostring.%d.%x"
66)
67
68// StringSym returns a symbol containing the string s.
69// The symbol contains the string data, not a string header.
70func StringSym(pos src.XPos, s string) (data *obj.LSym) {
71	var symname string
72	if len(s) > 100 {
73		// Huge strings are hashed to avoid long names in object files.
74		// Indulge in some paranoia by writing the length of s, too,
75		// as protection against length extension attacks.
76		// Same pattern is known to fileStringSym below.
77		h := sha256.New()
78		io.WriteString(h, s)
79		symname = fmt.Sprintf(stringSymPattern, len(s), h.Sum(nil))
80	} else {
81		// Small strings get named directly by their contents.
82		symname = strconv.Quote(s)
83	}
84
85	symdata := base.Ctxt.Lookup(stringSymPrefix + symname)
86	if !symdata.OnList() {
87		off := dstringdata(symdata, 0, s, pos, "string")
88		objw.Global(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
89		symdata.Set(obj.AttrContentAddressable, true)
90	}
91
92	return symdata
93}
94
95// maxFileSize is the maximum file size permitted by the linker
96// (see issue #9862).
97const maxFileSize = int64(2e9)
98
99// fileStringSym returns a symbol for the contents and the size of file.
100// If readonly is true, the symbol shares storage with any literal string
101// or other file with the same content and is placed in a read-only section.
102// If readonly is false, the symbol is a read-write copy separate from any other,
103// for use as the backing store of a []byte.
104// The content hash of file is copied into hash. (If hash is nil, nothing is copied.)
105// The returned symbol contains the data itself, not a string header.
106func fileStringSym(pos src.XPos, file string, readonly bool, hash []byte) (*obj.LSym, int64, error) {
107	f, err := os.Open(file)
108	if err != nil {
109		return nil, 0, err
110	}
111	defer f.Close()
112	info, err := f.Stat()
113	if err != nil {
114		return nil, 0, err
115	}
116	if !info.Mode().IsRegular() {
117		return nil, 0, fmt.Errorf("not a regular file")
118	}
119	size := info.Size()
120	if size <= 1*1024 {
121		data, err := ioutil.ReadAll(f)
122		if err != nil {
123			return nil, 0, err
124		}
125		if int64(len(data)) != size {
126			return nil, 0, fmt.Errorf("file changed between reads")
127		}
128		var sym *obj.LSym
129		if readonly {
130			sym = StringSym(pos, string(data))
131		} else {
132			sym = slicedata(pos, string(data)).Linksym()
133		}
134		if len(hash) > 0 {
135			sum := sha256.Sum256(data)
136			copy(hash, sum[:])
137		}
138		return sym, size, nil
139	}
140	if size > maxFileSize {
141		// ggloblsym takes an int32,
142		// and probably the rest of the toolchain
143		// can't handle such big symbols either.
144		// See golang.org/issue/9862.
145		return nil, 0, fmt.Errorf("file too large (%d bytes > %d bytes)", size, maxFileSize)
146	}
147
148	// File is too big to read and keep in memory.
149	// Compute hash if needed for read-only content hashing or if the caller wants it.
150	var sum []byte
151	if readonly || len(hash) > 0 {
152		h := sha256.New()
153		n, err := io.Copy(h, f)
154		if err != nil {
155			return nil, 0, err
156		}
157		if n != size {
158			return nil, 0, fmt.Errorf("file changed between reads")
159		}
160		sum = h.Sum(nil)
161		copy(hash, sum)
162	}
163
164	var symdata *obj.LSym
165	if readonly {
166		symname := fmt.Sprintf(stringSymPattern, size, sum)
167		symdata = base.Ctxt.Lookup(stringSymPrefix + symname)
168		if !symdata.OnList() {
169			info := symdata.NewFileInfo()
170			info.Name = file
171			info.Size = size
172			objw.Global(symdata, int32(size), obj.DUPOK|obj.RODATA|obj.LOCAL)
173			// Note: AttrContentAddressable cannot be set here,
174			// because the content-addressable-handling code
175			// does not know about file symbols.
176		}
177	} else {
178		// Emit a zero-length data symbol
179		// and then fix up length and content to use file.
180		symdata = slicedata(pos, "").Linksym()
181		symdata.Size = size
182		symdata.Type = objabi.SNOPTRDATA
183		info := symdata.NewFileInfo()
184		info.Name = file
185		info.Size = size
186	}
187
188	return symdata, size, nil
189}
190
191var slicedataGen int
192
193func slicedata(pos src.XPos, s string) *ir.Name {
194	slicedataGen++
195	symname := fmt.Sprintf(".gobytes.%d", slicedataGen)
196	sym := types.LocalPkg.Lookup(symname)
197	symnode := typecheck.NewName(sym)
198	sym.Def = symnode
199
200	lsym := symnode.Linksym()
201	off := dstringdata(lsym, 0, s, pos, "slice")
202	objw.Global(lsym, int32(off), obj.NOPTR|obj.LOCAL)
203
204	return symnode
205}
206
207func dstringdata(s *obj.LSym, off int, t string, pos src.XPos, what string) int {
208	// Objects that are too large will cause the data section to overflow right away,
209	// causing a cryptic error message by the linker. Check for oversize objects here
210	// and provide a useful error message instead.
211	if int64(len(t)) > 2e9 {
212		base.ErrorfAt(pos, "%v with length %v is too big", what, len(t))
213		return 0
214	}
215
216	s.WriteString(base.Ctxt, int64(off), len(t), t)
217	return off + len(t)
218}
219
220var (
221	funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym)
222	funcsyms   []*ir.Name // functions that need function value symbols
223)
224
225// FuncLinksym returns n·f, the function value symbol for n.
226func FuncLinksym(n *ir.Name) *obj.LSym {
227	if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
228		base.Fatalf("expected func name: %v", n)
229	}
230	s := n.Sym()
231
232	// funcsymsmu here serves to protect not just mutations of funcsyms (below),
233	// but also the package lookup of the func sym name,
234	// since this function gets called concurrently from the backend.
235	// There are no other concurrent package lookups in the backend,
236	// except for the types package, which is protected separately.
237	// Reusing funcsymsmu to also cover this package lookup
238	// avoids a general, broader, expensive package lookup mutex.
239	// Note NeedFuncSym also does package look-up of func sym names,
240	// but that it is only called serially, from the front end.
241	funcsymsmu.Lock()
242	sf, existed := s.Pkg.LookupOK(ir.FuncSymName(s))
243	// Don't export s·f when compiling for dynamic linking.
244	// When dynamically linking, the necessary function
245	// symbols will be created explicitly with NeedFuncSym.
246	// See the NeedFuncSym comment for details.
247	if !base.Ctxt.Flag_dynlink && !existed {
248		funcsyms = append(funcsyms, n)
249	}
250	funcsymsmu.Unlock()
251
252	return sf.Linksym()
253}
254
255func GlobalLinksym(n *ir.Name) *obj.LSym {
256	if n.Op() != ir.ONAME || n.Class != ir.PEXTERN {
257		base.Fatalf("expected global variable: %v", n)
258	}
259	return n.Linksym()
260}
261
262// NeedFuncSym ensures that fn·f is exported, if needed.
263// It is only used with -dynlink.
264// When not compiling for dynamic linking,
265// the funcsyms are created as needed by
266// the packages that use them.
267// Normally we emit the fn·f stubs as DUPOK syms,
268// but DUPOK doesn't work across shared library boundaries.
269// So instead, when dynamic linking, we only create
270// the fn·f stubs in fn's package.
271func NeedFuncSym(fn *ir.Func) {
272	if base.Ctxt.InParallel {
273		// The append below probably just needs to lock
274		// funcsymsmu, like in FuncSym.
275		base.Fatalf("NeedFuncSym must be called in serial")
276	}
277	if fn.ABI != obj.ABIInternal && buildcfg.Experiment.RegabiWrappers {
278		// Function values must always reference ABIInternal
279		// entry points, so it doesn't make sense to create a
280		// funcsym for other ABIs.
281		//
282		// (If we're not using ABI wrappers, it doesn't matter.)
283		base.Fatalf("expected ABIInternal: %v has %v", fn.Nname, fn.ABI)
284	}
285	if ir.IsBlank(fn.Nname) {
286		// Blank functions aren't unique, so we can't make a
287		// funcsym for them.
288		base.Fatalf("NeedFuncSym called for _")
289	}
290	if !base.Ctxt.Flag_dynlink {
291		return
292	}
293	s := fn.Nname.Sym()
294	if base.Flag.CompilingRuntime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") ||
295		(base.Ctxt.Pkgpath == "internal/abi" && (s.Name == "FuncPCABI0" || s.Name == "FuncPCABIInternal")) {
296		// runtime.getg(), getclosureptr(), getcallerpc(), getcallersp(),
297		// and internal/abi.FuncPCABIxxx() are not real functions and so
298		// do not get funcsyms.
299		return
300	}
301	funcsyms = append(funcsyms, fn.Nname)
302}
303
304func WriteFuncSyms() {
305	sort.Slice(funcsyms, func(i, j int) bool {
306		return funcsyms[i].Linksym().Name < funcsyms[j].Linksym().Name
307	})
308	for _, nam := range funcsyms {
309		s := nam.Sym()
310		sf := s.Pkg.Lookup(ir.FuncSymName(s)).Linksym()
311		// Function values must always reference ABIInternal
312		// entry points.
313		target := s.Linksym()
314		if target.ABI() != obj.ABIInternal {
315			base.Fatalf("expected ABIInternal: %v has %v", target, target.ABI())
316		}
317		objw.SymPtr(sf, 0, target, 0)
318		objw.Global(sf, int32(types.PtrSize), obj.DUPOK|obj.RODATA)
319	}
320}
321
322// InitConst writes the static literal c to n.
323// Neither n nor c is modified.
324func InitConst(n *ir.Name, noff int64, c ir.Node, wid int) {
325	if n.Op() != ir.ONAME {
326		base.Fatalf("InitConst n op %v", n.Op())
327	}
328	if n.Sym() == nil {
329		base.Fatalf("InitConst nil n sym")
330	}
331	if c.Op() == ir.ONIL {
332		return
333	}
334	if c.Op() != ir.OLITERAL {
335		base.Fatalf("InitConst c op %v", c.Op())
336	}
337	s := n.Linksym()
338	switch u := c.Val(); u.Kind() {
339	case constant.Bool:
340		i := int64(obj.Bool2int(constant.BoolVal(u)))
341		s.WriteInt(base.Ctxt, noff, wid, i)
342
343	case constant.Int:
344		s.WriteInt(base.Ctxt, noff, wid, ir.IntVal(c.Type(), u))
345
346	case constant.Float:
347		f, _ := constant.Float64Val(u)
348		switch c.Type().Kind() {
349		case types.TFLOAT32:
350			s.WriteFloat32(base.Ctxt, noff, float32(f))
351		case types.TFLOAT64:
352			s.WriteFloat64(base.Ctxt, noff, f)
353		}
354
355	case constant.Complex:
356		re, _ := constant.Float64Val(constant.Real(u))
357		im, _ := constant.Float64Val(constant.Imag(u))
358		switch c.Type().Kind() {
359		case types.TCOMPLEX64:
360			s.WriteFloat32(base.Ctxt, noff, float32(re))
361			s.WriteFloat32(base.Ctxt, noff+4, float32(im))
362		case types.TCOMPLEX128:
363			s.WriteFloat64(base.Ctxt, noff, re)
364			s.WriteFloat64(base.Ctxt, noff+8, im)
365		}
366
367	case constant.String:
368		i := constant.StringVal(u)
369		symdata := StringSym(n.Pos(), i)
370		s.WriteAddr(base.Ctxt, noff, types.PtrSize, symdata, 0)
371		s.WriteInt(base.Ctxt, noff+int64(types.PtrSize), types.PtrSize, int64(len(i)))
372
373	default:
374		base.Fatalf("InitConst unhandled OLITERAL %v", c)
375	}
376}
377