pcln.go 11.2 KB
Newer Older
1
// Copyright 2013 The Go Authors. All rights reserved.
2 3 4 5 6 7 8 9
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ld

import (
	"cmd/internal/obj"
	"log"
10 11
	"os"
	"path/filepath"
12 13 14 15 16
)

// iteration over encoded pcdata tables.

func getvarint(pp *[]byte) uint32 {
Russ Cox's avatar
Russ Cox committed
17 18 19
	v := uint32(0)
	p := *pp
	for shift := 0; ; shift += 7 {
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
		v |= uint32(p[0]&0x7F) << uint(shift)
		tmp4 := p
		p = p[1:]
		if tmp4[0]&0x80 == 0 {
			break
		}
	}

	*pp = p
	return v
}

func pciternext(it *Pciter) {
	it.pc = it.nextpc
	if it.done != 0 {
		return
	}
	if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) {
		it.done = 1
		return
	}

	// value delta
Russ Cox's avatar
Russ Cox committed
43
	v := getvarint(&it.p)
44 45 46 47 48 49 50

	if v == 0 && it.start == 0 {
		it.done = 1
		return
	}

	it.start = 0
Russ Cox's avatar
Russ Cox committed
51
	dv := int32(v>>1) ^ (int32(v<<31) >> 31)
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
	it.value += dv

	// pc delta
	v = getvarint(&it.p)

	it.nextpc = it.pc + v*it.pcscale
}

func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) {
	it.d = *d
	it.p = it.d.P
	it.pc = 0
	it.nextpc = 0
	it.value = -1
	it.start = 1
	it.done = 0
68
	it.pcscale = uint32(ctxt.Arch.MinLC)
69 70 71 72
	pciternext(it)
}

func addvarint(d *Pcdata, val uint32) {
Russ Cox's avatar
Russ Cox committed
73 74
	n := int32(0)
	for v := val; v >= 0x80; v >>= 7 {
75 76 77 78 79 80 81 82 83 84
		n++
	}
	n++

	old := len(d.P)
	for cap(d.P) < len(d.P)+int(n) {
		d.P = append(d.P[:cap(d.P)], 0)
	}
	d.P = d.P[:old+int(n)]

Russ Cox's avatar
Russ Cox committed
85 86
	p := d.P[old:]
	var v uint32
87 88 89 90 91 92 93
	for v = val; v >= 0x80; v >>= 7 {
		p[0] = byte(v | 0x80)
		p = p[1:]
	}
	p[0] = byte(v)
}

94
func addpctab(ctxt *Link, ftab *Symbol, off int32, d *Pcdata) int32 {
95 96 97
	var start int32
	if len(d.P) > 0 {
		start = int32(len(ftab.P))
98
		Addbytes(ftab, d.P)
99
	}
100
	return int32(setuint32(ctxt, ftab, int64(off), uint32(start)))
101 102
}

103
func ftabaddstring(ctxt *Link, ftab *Symbol, s string) int32 {
Russ Cox's avatar
Russ Cox committed
104 105
	n := int32(len(s)) + 1
	start := int32(len(ftab.P))
106
	Symgrow(ftab, int64(start)+int64(n)+1)
107 108 109 110
	copy(ftab.P[start:], s)
	return start
}

111 112
func renumberfiles(ctxt *Link, files []*Symbol, d *Pcdata) {
	var f *Symbol
113 114

	// Give files numbers.
Russ Cox's avatar
Russ Cox committed
115
	for i := 0; i < len(files); i++ {
116
		f = files[i]
117
		if f.Type != obj.SFILEPATH {
118 119
			ctxt.Filesyms = append(ctxt.Filesyms, f)
			f.Value = int64(len(ctxt.Filesyms))
120
			f.Type = obj.SFILEPATH
121
			f.Name = expandGoroot(f.Name)
122 123 124
		}
	}

Russ Cox's avatar
Russ Cox committed
125
	newval := int32(-1)
Russ Cox's avatar
Russ Cox committed
126
	var out Pcdata
Russ Cox's avatar
Russ Cox committed
127
	var it Pciter
128 129
	for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) {
		// value delta
130
		oldval := it.value
131

132
		var val int32
133 134 135 136 137 138 139 140 141
		if oldval == -1 {
			val = -1
		} else {
			if oldval < 0 || oldval >= int32(len(files)) {
				log.Fatalf("bad pcdata %d", oldval)
			}
			val = int32(files[oldval].Value)
		}

142
		dv := val - newval
143
		newval = val
144
		v := (uint32(dv) << 1) ^ uint32(dv>>31)
145 146 147 148 149 150 151 152 153 154 155 156
		addvarint(&out, v)

		// pc delta
		addvarint(&out, (it.nextpc-it.pc)/it.pcscale)
	}

	// terminating value delta
	addvarint(&out, 0)

	*d = out
}

157 158 159 160 161 162 163 164 165 166
// onlycsymbol reports whether this is a cgo symbol provided by the
// runtime and only used from C code.
func onlycsymbol(s *Symbol) bool {
	switch s.Name {
	case "_cgo_topofstack", "_cgo_panic", "crosscall2":
		return true
	}
	return false
}

167
func container(s *Symbol) int {
168 169 170
	if s == nil {
		return 0
	}
171
	if Buildmode == BuildmodePlugin && Headtype == obj.Hdarwin && onlycsymbol(s) {
172 173
		return 1
	}
174 175
	// We want to generate func table entries only for the "lowest level" symbols,
	// not containers of subsymbols.
176
	if s.Type&obj.SCONTAINER != 0 {
177 178 179 180 181 182 183 184
		return 1
	}
	return 0
}

// pclntab initializes the pclntab symbol with
// runtime function and file name information.

185
var pclntabZpcln FuncInfo
186

187
// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
188 189 190
var pclntabNfunc int32
var pclntabFiletabOffset int32
var pclntabPclntabOffset int32
191 192
var pclntabFirstFunc *Symbol
var pclntabLastFunc *Symbol
193

194
func (ctxt *Link) pclntab() {
195
	funcdataBytes := int64(0)
196
	ftab := ctxt.Syms.Lookup("runtime.pclntab", 0)
197
	ftab.Type = obj.SPCLNTAB
198
	ftab.Attr |= AttrReachable
199 200 201 202 203 204 205

	// See golang.org/s/go12symtab for the format. Briefly:
	//	8-byte header
	//	nfunc [thearch.ptrsize bytes]
	//	function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
	//	end PC [thearch.ptrsize bytes]
	//	offset to file table [4 bytes]
Russ Cox's avatar
Russ Cox committed
206
	nfunc := int32(0)
207

208
	// Find container symbols, mark them with SCONTAINER
209
	for _, s := range ctxt.Textp {
210 211
		if s.Outer != nil {
			s.Outer.Type |= obj.SCONTAINER
212 213 214
		}
	}

215
	for _, s := range ctxt.Textp {
216
		if container(s) == 0 {
217 218 219 220
			nfunc++
		}
	}

221
	pclntabNfunc = nfunc
222
	Symgrow(ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize)+4)
223 224 225 226
	setuint32(ctxt, ftab, 0, 0xfffffffb)
	setuint8(ctxt, ftab, 6, uint8(SysArch.MinLC))
	setuint8(ctxt, ftab, 7, uint8(SysArch.PtrSize))
	setuintxx(ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize))
227
	pclntabPclntabOffset = int32(8 + SysArch.PtrSize)
228 229

	nfunc = 0
230
	var last *Symbol
David Crawshaw's avatar
David Crawshaw committed
231 232 233
	for _, s := range ctxt.Textp {
		last = s
		if container(s) != 0 {
234 235
			continue
		}
David Crawshaw's avatar
David Crawshaw committed
236
		pcln := s.FuncInfo
237
		if pcln == nil {
238
			pcln = &pclntabZpcln
239 240
		}

241
		if pclntabFirstFunc == nil {
David Crawshaw's avatar
David Crawshaw committed
242
			pclntabFirstFunc = s
243 244
		}

245
		funcstart := int32(len(ftab.P))
246
		funcstart += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1)
247

David Crawshaw's avatar
David Crawshaw committed
248
		setaddr(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), s)
249
		setuintxx(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint64(funcstart), int64(SysArch.PtrSize))
250

251 252 253
		// Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
		// and package debug/gosym.

254
		// fixed size of struct, checked below
255
		off := funcstart
256

257
		end := funcstart + int32(SysArch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(SysArch.PtrSize)
258
		if len(pcln.Funcdata) > 0 && (end&int32(SysArch.PtrSize-1) != 0) {
259 260
			end += 4
		}
261
		Symgrow(ftab, int64(end))
262 263

		// entry uintptr
David Crawshaw's avatar
David Crawshaw committed
264
		off = int32(setaddr(ctxt, ftab, int64(off), s))
265 266

		// name int32
David Crawshaw's avatar
David Crawshaw committed
267
		off = int32(setuint32(ctxt, ftab, int64(off), uint32(ftabaddstring(ctxt, ftab, s.Name))))
268 269 270

		// args int32
		// TODO: Move into funcinfo.
271
		args := uint32(0)
David Crawshaw's avatar
David Crawshaw committed
272 273
		if s.FuncInfo != nil {
			args = uint32(s.FuncInfo.Args)
274
		}
275
		off = int32(setuint32(ctxt, ftab, int64(off), args))
276 277

		// frame int32
278 279 280 281
		// This has been removed (it was never set quite correctly anyway).
		// Nothing should use it.
		// Leave an obviously incorrect value.
		// TODO: Remove entirely.
282
		off = int32(setuint32(ctxt, ftab, int64(off), 0x1234567))
283

284
		if pcln != &pclntabZpcln {
285
			renumberfiles(ctxt, pcln.File, &pcln.Pcfile)
286 287
			if false {
				// Sanity check the new numbering
288
				var it Pciter
289 290
				for pciterinit(ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
					if it.value < 1 || it.value > int32(len(ctxt.Filesyms)) {
David Crawshaw's avatar
David Crawshaw committed
291
						Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.value, len(ctxt.Filesyms))
292
						errorexit()
293 294 295 296 297 298
					}
				}
			}
		}

		// pcdata
299
		off = addpctab(ctxt, ftab, off, &pcln.Pcsp)
300

301 302 303 304
		off = addpctab(ctxt, ftab, off, &pcln.Pcfile)
		off = addpctab(ctxt, ftab, off, &pcln.Pcline)
		off = int32(setuint32(ctxt, ftab, int64(off), uint32(len(pcln.Pcdata))))
		off = int32(setuint32(ctxt, ftab, int64(off), uint32(len(pcln.Funcdata))))
305
		for i := 0; i < len(pcln.Pcdata); i++ {
306
			off = addpctab(ctxt, ftab, off, &pcln.Pcdata[i])
307 308 309 310
		}

		// funcdata, must be pointer-aligned and we're only int32-aligned.
		// Missing funcdata will be 0 (nil pointer).
311
		if len(pcln.Funcdata) > 0 {
312
			if off&int32(SysArch.PtrSize-1) != 0 {
313 314
				off += 4
			}
315
			for i := 0; i < len(pcln.Funcdata); i++ {
316
				if pcln.Funcdata[i] == nil {
317
					setuintxx(ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(SysArch.PtrSize))
318 319
				} else {
					// TODO: Dedup.
320
					funcdataBytes += pcln.Funcdata[i].Size
321

322
					setaddrplus(ctxt, ftab, int64(off)+int64(SysArch.PtrSize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
323 324 325
				}
			}

326
			off += int32(len(pcln.Funcdata)) * int32(SysArch.PtrSize)
327 328 329
		}

		if off != end {
David Crawshaw's avatar
David Crawshaw committed
330
			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), SysArch.PtrSize)
331
			errorexit()
332 333 334 335 336
		}

		nfunc++
	}

337
	pclntabLastFunc = last
338
	// Final entry of table is just end pc.
339
	setaddrplus(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize), last, last.Size)
340 341

	// Start file table.
Russ Cox's avatar
Russ Cox committed
342
	start := int32(len(ftab.P))
343

344
	start += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1)
345
	pclntabFiletabOffset = start
346
	setuint32(ctxt, ftab, 8+int64(SysArch.PtrSize)+int64(nfunc)*2*int64(SysArch.PtrSize)+int64(SysArch.PtrSize), uint32(start))
347

348
	Symgrow(ftab, int64(start)+(int64(len(ctxt.Filesyms))+1)*4)
349
	setuint32(ctxt, ftab, int64(start), uint32(len(ctxt.Filesyms)+1))
350 351 352
	for i := len(ctxt.Filesyms) - 1; i >= 0; i-- {
		s := ctxt.Filesyms[i]
		setuint32(ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ctxt, ftab, s.Name)))
353 354 355 356
	}

	ftab.Size = int64(len(ftab.P))

357
	if ctxt.Debugvlog != 0 {
358
		ctxt.Logf("%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), ftab.Size, funcdataBytes)
359 360 361
	}
}

362 363 364
func expandGoroot(s string) string {
	const n = len("$GOROOT")
	if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
365
		root := obj.GOROOT
366 367 368 369 370 371 372 373
		if final := os.Getenv("GOROOT_FINAL"); final != "" {
			root = final
		}
		return filepath.ToSlash(filepath.Join(root, s[n:]))
	}
	return s
}

374 375 376 377 378 379 380 381
const (
	BUCKETSIZE    = 256 * MINFUNC
	SUBBUCKETS    = 16
	SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
	NOIDX         = 0x7fffffff
)

// findfunctab generates a lookup table to quickly find the containing
382
// function for a pc. See src/runtime/symtab.go:findfunc for details.
383
func (ctxt *Link) findfunctab() {
384
	t := ctxt.Syms.Lookup("runtime.findfunctab", 0)
385
	t.Type = obj.SRODATA
386 387
	t.Attr |= AttrReachable
	t.Attr |= AttrLocal
388 389

	// find min and max address
390
	min := ctxt.Textp[0].Value
Russ Cox's avatar
Russ Cox committed
391
	max := int64(0)
392
	for _, s := range ctxt.Textp {
393 394 395 396 397
		max = s.Value + s.Size
	}

	// for each subbucket, compute the minimum of all symbol indexes
	// that map to that subbucket.
Russ Cox's avatar
Russ Cox committed
398
	n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
399

Russ Cox's avatar
Russ Cox committed
400 401
	indexes := make([]int32, n)
	for i := int32(0); i < n; i++ {
402 403
		indexes[i] = NOIDX
	}
Russ Cox's avatar
Russ Cox committed
404
	idx := int32(0)
405
	for i, s := range ctxt.Textp {
406 407 408
		if container(s) != 0 {
			continue
		}
409
		p := s.Value
410
		var e *Symbol
411
		i++
412 413
		if i < len(ctxt.Textp) {
			e = ctxt.Textp[i]
414
		}
415 416
		for container(e) != 0 && i < len(ctxt.Textp) {
			e = ctxt.Textp[i]
417
			i++
418
		}
419
		q := max
420 421 422 423 424 425
		if e != nil {
			q = e.Value
		}

		//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
		for ; p < q; p += SUBBUCKETSIZE {
426
			i = int((p - min) / SUBBUCKETSIZE)
427 428 429 430 431
			if indexes[i] > idx {
				indexes[i] = idx
			}
		}

432
		i = int((q - 1 - min) / SUBBUCKETSIZE)
433 434 435 436 437 438 439
		if indexes[i] > idx {
			indexes[i] = idx
		}
		idx++
	}

	// allocate table
Russ Cox's avatar
Russ Cox committed
440
	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
441

442
	Symgrow(t, 4*int64(nbuckets)+int64(n))
443 444

	// fill in table
Russ Cox's avatar
Russ Cox committed
445
	for i := int32(0); i < nbuckets; i++ {
446
		base := indexes[i*SUBBUCKETS]
447
		if base == NOIDX {
448
			Errorf(nil, "hole in findfunctab")
449
		}
450
		setuint32(ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
451
		for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
452 453
			idx = indexes[i*SUBBUCKETS+j]
			if idx == NOIDX {
454
				Errorf(nil, "hole in findfunctab")
455 456
			}
			if idx-base >= 256 {
457
				Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
458 459
			}

460
			setuint8(ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
461 462 463
		}
	}
}