1// Copyright 2013 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 ld
6
7import (
8	"cmd/internal/obj"
9	"cmd/internal/objabi"
10	"cmd/internal/src"
11	"cmd/internal/sys"
12	"cmd/link/internal/sym"
13	"encoding/binary"
14	"fmt"
15	"log"
16	"os"
17	"path/filepath"
18	"strings"
19)
20
21func ftabaddstring(ftab *sym.Symbol, s string) int32 {
22	start := len(ftab.P)
23	ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
24	copy(ftab.P[start:], s)
25	return int32(start)
26}
27
28// numberfile assigns a file number to the file if it hasn't been assigned already.
29func numberfile(ctxt *Link, file *sym.Symbol) {
30	if file.Type != sym.SFILEPATH {
31		ctxt.Filesyms = append(ctxt.Filesyms, file)
32		file.Value = int64(len(ctxt.Filesyms))
33		file.Type = sym.SFILEPATH
34		path := file.Name[len(src.FileSymPrefix):]
35		file.Name = expandGoroot(path)
36	}
37}
38
39func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) {
40	// Give files numbers.
41	for _, f := range files {
42		numberfile(ctxt, f)
43	}
44
45	buf := make([]byte, binary.MaxVarintLen32)
46	newval := int32(-1)
47	var out sym.Pcdata
48	it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
49	for it.Init(d.P); !it.Done; it.Next() {
50		// value delta
51		oldval := it.Value
52
53		var val int32
54		if oldval == -1 {
55			val = -1
56		} else {
57			if oldval < 0 || oldval >= int32(len(files)) {
58				log.Fatalf("bad pcdata %d", oldval)
59			}
60			val = int32(files[oldval].Value)
61		}
62
63		dv := val - newval
64		newval = val
65
66		// value
67		n := binary.PutVarint(buf, int64(dv))
68		out.P = append(out.P, buf[:n]...)
69
70		// pc delta
71		pc := (it.NextPC - it.PC) / it.PCScale
72		n = binary.PutUvarint(buf, uint64(pc))
73		out.P = append(out.P, buf[:n]...)
74	}
75
76	// terminating value delta
77	// we want to write varint-encoded 0, which is just 0
78	out.P = append(out.P, 0)
79
80	*d = out
81}
82
83// onlycsymbol reports whether this is a symbol that is referenced by C code.
84func onlycsymbol(s *sym.Symbol) bool {
85	switch s.Name {
86	case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
87		return true
88	}
89	if strings.HasPrefix(s.Name, "_cgoexp_") {
90		return true
91	}
92	return false
93}
94
95func emitPcln(ctxt *Link, s *sym.Symbol) bool {
96	if s == nil {
97		return true
98	}
99	if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) {
100		return false
101	}
102	// We want to generate func table entries only for the "lowest level" symbols,
103	// not containers of subsymbols.
104	return !s.Attr.Container()
105}
106
107// pclntab initializes the pclntab symbol with
108// runtime function and file name information.
109
110var pclntabZpcln sym.FuncInfo
111
112// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
113var pclntabNfunc int32
114var pclntabFiletabOffset int32
115var pclntabPclntabOffset int32
116var pclntabFirstFunc *sym.Symbol
117var pclntabLastFunc *sym.Symbol
118
119func (ctxt *Link) pclntab() {
120	funcdataBytes := int64(0)
121	ftab := ctxt.Syms.Lookup("runtime.pclntab", 0)
122	ftab.Type = sym.SPCLNTAB
123	ftab.Attr |= sym.AttrReachable
124
125	// See golang.org/s/go12symtab for the format. Briefly:
126	//	8-byte header
127	//	nfunc [thearch.ptrsize bytes]
128	//	function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
129	//	end PC [thearch.ptrsize bytes]
130	//	offset to file table [4 bytes]
131
132	// Find container symbols and mark them as such.
133	for _, s := range ctxt.Textp {
134		if s.Outer != nil {
135			s.Outer.Attr |= sym.AttrContainer
136		}
137	}
138
139	// Gather some basic stats and info.
140	var nfunc int32
141	prevSect := ctxt.Textp[0].Sect
142	for _, s := range ctxt.Textp {
143		if !emitPcln(ctxt, s) {
144			continue
145		}
146		nfunc++
147		if pclntabFirstFunc == nil {
148			pclntabFirstFunc = s
149		}
150		if s.Sect != prevSect {
151			// With multiple text sections, the external linker may insert functions
152			// between the sections, which are not known by Go. This leaves holes in
153			// the PC range covered by the func table. We need to generate an entry
154			// to mark the hole.
155			nfunc++
156			prevSect = s.Sect
157		}
158	}
159
160	pclntabNfunc = nfunc
161	ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
162	ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb)
163	ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
164	ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
165	ftab.SetUint(ctxt.Arch, 8, uint64(nfunc))
166	pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize)
167
168	funcnameoff := make(map[string]int32)
169	nameToOffset := func(name string) int32 {
170		nameoff, ok := funcnameoff[name]
171		if !ok {
172			nameoff = ftabaddstring(ftab, name)
173			funcnameoff[name] = nameoff
174		}
175		return nameoff
176	}
177
178	pctaboff := make(map[string]uint32)
179	writepctab := func(off int32, p []byte) int32 {
180		start, ok := pctaboff[string(p)]
181		if !ok {
182			if len(p) > 0 {
183				start = uint32(len(ftab.P))
184				ftab.AddBytes(p)
185			}
186			pctaboff[string(p)] = start
187		}
188		newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start))
189		return newoff
190	}
191
192	nfunc = 0 // repurpose nfunc as a running index
193	prevFunc := ctxt.Textp[0]
194	for _, s := range ctxt.Textp {
195		if !emitPcln(ctxt, s) {
196			continue
197		}
198
199		if s.Sect != prevFunc.Sect {
200			// With multiple text sections, there may be a hole here in the address
201			// space (see the comment above). We use an invalid funcoff value to
202			// mark the hole.
203			// See also runtime/symtab.go:findfunc
204			ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFunc.Size)
205			ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0))
206			nfunc++
207		}
208		prevFunc = s
209
210		pcln := s.FuncInfo
211		if pcln == nil {
212			pcln = &pclntabZpcln
213		}
214
215		if len(pcln.InlTree) > 0 {
216			if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex {
217				// Create inlining pcdata table.
218				pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
219				copy(pcdata, pcln.Pcdata)
220				pcln.Pcdata = pcdata
221			}
222
223			if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree {
224				// Create inline tree funcdata.
225				funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1)
226				funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
227				copy(funcdata, pcln.Funcdata)
228				copy(funcdataoff, pcln.Funcdataoff)
229				pcln.Funcdata = funcdata
230				pcln.Funcdataoff = funcdataoff
231			}
232		}
233
234		funcstart := int32(len(ftab.P))
235		funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
236
237		ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s)
238		ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
239
240		// Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
241		// and package debug/gosym.
242
243		// fixed size of struct, checked below
244		off := funcstart
245
246		end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize)
247		if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
248			end += 4
249		}
250		ftab.Grow(int64(end))
251
252		// entry uintptr
253		off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s))
254
255		// name int32
256		nameoff := nameToOffset(s.Name)
257		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
258
259		// args int32
260		// TODO: Move into funcinfo.
261		args := uint32(0)
262		if s.FuncInfo != nil {
263			args = uint32(s.FuncInfo.Args)
264		}
265		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
266
267		// deferreturn
268		deferreturn := uint32(0)
269		lastWasmAddr := uint32(0)
270		for _, r := range s.R {
271			if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR {
272				// Wasm does not have a live variable set at the deferreturn
273				// call itself. Instead it has one identified by the
274				// resumption point immediately preceding the deferreturn.
275				// The wasm code has a R_ADDR relocation which is used to
276				// set the resumption point to PC_B.
277				lastWasmAddr = uint32(r.Add)
278			}
279			if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
280				if ctxt.Arch.Family == sys.Wasm {
281					deferreturn = lastWasmAddr - 1
282				} else {
283					// Note: the relocation target is in the call instruction, but
284					// is not necessarily the whole instruction (for instance, on
285					// x86 the relocation applies to bytes [1:5] of the 5 byte call
286					// instruction).
287					deferreturn = uint32(r.Off)
288					switch ctxt.Arch.Family {
289					case sys.AMD64, sys.I386:
290						deferreturn--
291					case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
292						// no change
293					case sys.RISCV64:
294						// TODO(jsing): The JALR instruction is marked with
295						// R_CALLRISCV, whereas the actual reloc is currently
296						// one instruction earlier starting with the AUIPC.
297						deferreturn -= 4
298					case sys.S390X:
299						deferreturn -= 2
300					default:
301						panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family))
302					}
303				}
304				break // only need one
305			}
306		}
307		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
308
309		if pcln != &pclntabZpcln {
310			renumberfiles(ctxt, pcln.File, &pcln.Pcfile)
311			if false {
312				// Sanity check the new numbering
313				it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
314				for it.Init(pcln.Pcfile.P); !it.Done; it.Next() {
315					if it.Value < 1 || it.Value > int32(len(ctxt.Filesyms)) {
316						Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(ctxt.Filesyms))
317						errorexit()
318					}
319				}
320			}
321		}
322
323		if len(pcln.InlTree) > 0 {
324			inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0)
325			inlTreeSym.Type = sym.SRODATA
326			inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK
327
328			for i, call := range pcln.InlTree {
329				// Usually, call.File is already numbered since the file
330				// shows up in the Pcfile table. However, two inlined calls
331				// might overlap exactly so that only the innermost file
332				// appears in the Pcfile table. In that case, this assigns
333				// the outer file a number.
334				numberfile(ctxt, call.File)
335				nameoff := nameToOffset(call.Func)
336
337				inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
338				inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, "")))
339				// byte 3 is unused
340				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
341				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
342				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff))
343				inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC))
344			}
345
346			pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym
347			pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline
348		}
349
350		// pcdata
351		off = writepctab(off, pcln.Pcsp.P)
352		off = writepctab(off, pcln.Pcfile.P)
353		off = writepctab(off, pcln.Pcline.P)
354		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata))))
355
356		// funcID uint8
357		var file string
358		if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 {
359			file = s.FuncInfo.File[0].Name
360		}
361		funcID := objabi.GetFuncID(s.Name, file)
362
363		off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
364
365		// unused
366		off += 2
367
368		// nfuncdata must be the final entry.
369		off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata))))
370		for i := range pcln.Pcdata {
371			off = writepctab(off, pcln.Pcdata[i].P)
372		}
373
374		// funcdata, must be pointer-aligned and we're only int32-aligned.
375		// Missing funcdata will be 0 (nil pointer).
376		if len(pcln.Funcdata) > 0 {
377			if off&int32(ctxt.Arch.PtrSize-1) != 0 {
378				off += 4
379			}
380			for i := range pcln.Funcdata {
381				dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i)
382				if pcln.Funcdata[i] == nil {
383					ftab.SetUint(ctxt.Arch, dataoff, uint64(pcln.Funcdataoff[i]))
384					continue
385				}
386				// TODO: Dedup.
387				funcdataBytes += pcln.Funcdata[i].Size
388				ftab.SetAddrPlus(ctxt.Arch, dataoff, pcln.Funcdata[i], pcln.Funcdataoff[i])
389			}
390			off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize)
391		}
392
393		if off != end {
394			Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), ctxt.Arch.PtrSize)
395			errorexit()
396		}
397
398		nfunc++
399	}
400
401	last := ctxt.Textp[len(ctxt.Textp)-1]
402	pclntabLastFunc = last
403	// Final entry of table is just end pc.
404	ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size)
405
406	// Start file table.
407	start := int32(len(ftab.P))
408
409	start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1)
410	pclntabFiletabOffset = start
411	ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
412
413	ftab.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4)
414	ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1))
415	for i := len(ctxt.Filesyms) - 1; i >= 0; i-- {
416		s := ctxt.Filesyms[i]
417		ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
418	}
419
420	ftab.Size = int64(len(ftab.P))
421
422	if ctxt.Debugvlog != 0 {
423		ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size, funcdataBytes)
424	}
425}
426
427func gorootFinal() string {
428	root := objabi.GOROOT
429	if final := os.Getenv("GOROOT_FINAL"); final != "" {
430		root = final
431	}
432	return root
433}
434
435func expandGoroot(s string) string {
436	const n = len("$GOROOT")
437	if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
438		return filepath.ToSlash(filepath.Join(gorootFinal(), s[n:]))
439	}
440	return s
441}
442
443const (
444	BUCKETSIZE    = 256 * MINFUNC
445	SUBBUCKETS    = 16
446	SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
447	NOIDX         = 0x7fffffff
448)
449
450// findfunctab generates a lookup table to quickly find the containing
451// function for a pc. See src/runtime/symtab.go:findfunc for details.
452func (ctxt *Link) findfunctab() {
453	t := ctxt.Syms.Lookup("runtime.findfunctab", 0)
454	t.Type = sym.SRODATA
455	t.Attr |= sym.AttrReachable
456	t.Attr |= sym.AttrLocal
457
458	// find min and max address
459	min := ctxt.Textp[0].Value
460	lastp := ctxt.Textp[len(ctxt.Textp)-1]
461	max := lastp.Value + lastp.Size
462
463	// for each subbucket, compute the minimum of all symbol indexes
464	// that map to that subbucket.
465	n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
466
467	indexes := make([]int32, n)
468	for i := int32(0); i < n; i++ {
469		indexes[i] = NOIDX
470	}
471	idx := int32(0)
472	for i, s := range ctxt.Textp {
473		if !emitPcln(ctxt, s) {
474			continue
475		}
476		p := s.Value
477		var e *sym.Symbol
478		i++
479		if i < len(ctxt.Textp) {
480			e = ctxt.Textp[i]
481		}
482		for !emitPcln(ctxt, e) && i < len(ctxt.Textp) {
483			e = ctxt.Textp[i]
484			i++
485		}
486		q := max
487		if e != nil {
488			q = e.Value
489		}
490
491		//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
492		for ; p < q; p += SUBBUCKETSIZE {
493			i = int((p - min) / SUBBUCKETSIZE)
494			if indexes[i] > idx {
495				indexes[i] = idx
496			}
497		}
498
499		i = int((q - 1 - min) / SUBBUCKETSIZE)
500		if indexes[i] > idx {
501			indexes[i] = idx
502		}
503		idx++
504	}
505
506	// allocate table
507	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
508
509	t.Grow(4*int64(nbuckets) + int64(n))
510
511	// fill in table
512	for i := int32(0); i < nbuckets; i++ {
513		base := indexes[i*SUBBUCKETS]
514		if base == NOIDX {
515			Errorf(nil, "hole in findfunctab")
516		}
517		t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base))
518		for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
519			idx = indexes[i*SUBBUCKETS+j]
520			if idx == NOIDX {
521				Errorf(nil, "hole in findfunctab")
522			}
523			if idx-base >= 256 {
524				Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
525			}
526
527			t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
528		}
529	}
530}
531