Home | History | Annotate | Download | only in ld
      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 
      5 package ld
      6 
      7 import (
      8 	"cmd/internal/obj"
      9 	"fmt"
     10 	"log"
     11 )
     12 
     13 // funcpctab writes to dst a pc-value table mapping the code in func to the values
     14 // returned by valfunc parameterized by arg. The invocation of valfunc to update the
     15 // current value is, for each p,
     16 //
     17 //	val = valfunc(func, val, p, 0, arg);
     18 //	record val as value at p->pc;
     19 //	val = valfunc(func, val, p, 1, arg);
     20 //
     21 // where func is the function, val is the current value, p is the instruction being
     22 // considered, and arg can be used to further parameterize valfunc.
     23 
     24 // pctofileline computes either the file number (arg == 0)
     25 // or the line number (arg == 1) to use at p.
     26 // Because p->lineno applies to p, phase == 0 (before p)
     27 // takes care of the update.
     28 
     29 // pctospadj computes the sp adjustment in effect.
     30 // It is oldval plus any adjustment made by p itself.
     31 // The adjustment by p takes effect only after p, so we
     32 // apply the change during phase == 1.
     33 
     34 // pctopcdata computes the pcdata value in effect at p.
     35 // A PCDATA instruction sets the value in effect at future
     36 // non-PCDATA instructions.
     37 // Since PCDATA instructions have no width in the final code,
     38 // it does not matter which phase we use for the update.
     39 
     40 // iteration over encoded pcdata tables.
     41 
     42 func getvarint(pp *[]byte) uint32 {
     43 	v := uint32(0)
     44 	p := *pp
     45 	for shift := 0; ; shift += 7 {
     46 		v |= uint32(p[0]&0x7F) << uint(shift)
     47 		tmp4 := p
     48 		p = p[1:]
     49 		if tmp4[0]&0x80 == 0 {
     50 			break
     51 		}
     52 	}
     53 
     54 	*pp = p
     55 	return v
     56 }
     57 
     58 func pciternext(it *Pciter) {
     59 	it.pc = it.nextpc
     60 	if it.done != 0 {
     61 		return
     62 	}
     63 	if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) {
     64 		it.done = 1
     65 		return
     66 	}
     67 
     68 	// value delta
     69 	v := getvarint(&it.p)
     70 
     71 	if v == 0 && it.start == 0 {
     72 		it.done = 1
     73 		return
     74 	}
     75 
     76 	it.start = 0
     77 	dv := int32(v>>1) ^ (int32(v<<31) >> 31)
     78 	it.value += dv
     79 
     80 	// pc delta
     81 	v = getvarint(&it.p)
     82 
     83 	it.nextpc = it.pc + v*it.pcscale
     84 }
     85 
     86 func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) {
     87 	it.d = *d
     88 	it.p = it.d.P
     89 	it.pc = 0
     90 	it.nextpc = 0
     91 	it.value = -1
     92 	it.start = 1
     93 	it.done = 0
     94 	it.pcscale = uint32(ctxt.Arch.Minlc)
     95 	pciternext(it)
     96 }
     97 
     98 // Copyright 2013 The Go Authors.  All rights reserved.
     99 // Use of this source code is governed by a BSD-style
    100 // license that can be found in the LICENSE file.
    101 
    102 func addvarint(d *Pcdata, val uint32) {
    103 	n := int32(0)
    104 	for v := val; v >= 0x80; v >>= 7 {
    105 		n++
    106 	}
    107 	n++
    108 
    109 	old := len(d.P)
    110 	for cap(d.P) < len(d.P)+int(n) {
    111 		d.P = append(d.P[:cap(d.P)], 0)
    112 	}
    113 	d.P = d.P[:old+int(n)]
    114 
    115 	p := d.P[old:]
    116 	var v uint32
    117 	for v = val; v >= 0x80; v >>= 7 {
    118 		p[0] = byte(v | 0x80)
    119 		p = p[1:]
    120 	}
    121 	p[0] = byte(v)
    122 }
    123 
    124 func addpctab(ftab *LSym, off int32, d *Pcdata) int32 {
    125 	var start int32
    126 	if len(d.P) > 0 {
    127 		start = int32(len(ftab.P))
    128 		Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P)))
    129 		copy(ftab.P[start:], d.P)
    130 	}
    131 	return int32(setuint32(Ctxt, ftab, int64(off), uint32(start)))
    132 }
    133 
    134 func ftabaddstring(ftab *LSym, s string) int32 {
    135 	n := int32(len(s)) + 1
    136 	start := int32(len(ftab.P))
    137 	Symgrow(Ctxt, ftab, int64(start)+int64(n)+1)
    138 	copy(ftab.P[start:], s)
    139 	return start
    140 }
    141 
    142 func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
    143 	var f *LSym
    144 
    145 	// Give files numbers.
    146 	for i := 0; i < len(files); i++ {
    147 		f = files[i]
    148 		if f.Type != obj.SFILEPATH {
    149 			ctxt.Nhistfile++
    150 			f.Value = int64(ctxt.Nhistfile)
    151 			f.Type = obj.SFILEPATH
    152 			f.Next = ctxt.Filesyms
    153 			ctxt.Filesyms = f
    154 		}
    155 	}
    156 
    157 	newval := int32(-1)
    158 	var out Pcdata
    159 
    160 	var dv int32
    161 	var it Pciter
    162 	var oldval int32
    163 	var v uint32
    164 	var val int32
    165 	for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) {
    166 		// value delta
    167 		oldval = it.value
    168 
    169 		if oldval == -1 {
    170 			val = -1
    171 		} else {
    172 			if oldval < 0 || oldval >= int32(len(files)) {
    173 				log.Fatalf("bad pcdata %d", oldval)
    174 			}
    175 			val = int32(files[oldval].Value)
    176 		}
    177 
    178 		dv = val - newval
    179 		newval = val
    180 		v = (uint32(dv) << 1) ^ uint32(int32(dv>>31))
    181 		addvarint(&out, v)
    182 
    183 		// pc delta
    184 		addvarint(&out, (it.nextpc-it.pc)/it.pcscale)
    185 	}
    186 
    187 	// terminating value delta
    188 	addvarint(&out, 0)
    189 
    190 	*d = out
    191 }
    192 
    193 func container(s *LSym) int {
    194 	// We want to generate func table entries only for the "lowest level" symbols,
    195 	// not containers of subsymbols.
    196 	if s != nil && s.Type&obj.SCONTAINER != 0 {
    197 		return 1
    198 	}
    199 	return 0
    200 }
    201 
    202 // pclntab initializes the pclntab symbol with
    203 // runtime function and file name information.
    204 
    205 var pclntab_zpcln Pcln
    206 
    207 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
    208 var pclntabNfunc int32
    209 var pclntabFiletabOffset int32
    210 var pclntabPclntabOffset int32
    211 var pclntabFirstFunc *LSym
    212 var pclntabLastFunc *LSym
    213 
    214 func pclntab() {
    215 	funcdata_bytes := int64(0)
    216 	ftab := Linklookup(Ctxt, "runtime.pclntab", 0)
    217 	ftab.Type = obj.SPCLNTAB
    218 	ftab.Reachable = true
    219 
    220 	// See golang.org/s/go12symtab for the format. Briefly:
    221 	//	8-byte header
    222 	//	nfunc [thearch.ptrsize bytes]
    223 	//	function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
    224 	//	end PC [thearch.ptrsize bytes]
    225 	//	offset to file table [4 bytes]
    226 	nfunc := int32(0)
    227 
    228 	// Find container symbols, mark them with SCONTAINER
    229 	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
    230 		if Ctxt.Cursym.Outer != nil {
    231 			Ctxt.Cursym.Outer.Type |= obj.SCONTAINER
    232 		}
    233 	}
    234 
    235 	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
    236 		if container(Ctxt.Cursym) == 0 {
    237 			nfunc++
    238 		}
    239 	}
    240 
    241 	pclntabNfunc = nfunc
    242 	Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4)
    243 	setuint32(Ctxt, ftab, 0, 0xfffffffb)
    244 	setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc))
    245 	setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize))
    246 	setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize))
    247 	pclntabPclntabOffset = int32(8 + Thearch.Ptrsize)
    248 
    249 	nfunc = 0
    250 	var last *LSym
    251 	var end int32
    252 	var funcstart int32
    253 	var i int32
    254 	var it Pciter
    255 	var off int32
    256 	var pcln *Pcln
    257 	for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
    258 		last = Ctxt.Cursym
    259 		if container(Ctxt.Cursym) != 0 {
    260 			continue
    261 		}
    262 		pcln = Ctxt.Cursym.Pcln
    263 		if pcln == nil {
    264 			pcln = &pclntab_zpcln
    265 		}
    266 
    267 		if pclntabFirstFunc == nil {
    268 			pclntabFirstFunc = Ctxt.Cursym
    269 		}
    270 
    271 		funcstart = int32(len(ftab.P))
    272 		funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
    273 
    274 		setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym)
    275 		setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize))
    276 
    277 		// fixed size of struct, checked below
    278 		off = funcstart
    279 
    280 		end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize)
    281 		if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) {
    282 			end += 4
    283 		}
    284 		Symgrow(Ctxt, ftab, int64(end))
    285 
    286 		// entry uintptr
    287 		off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym))
    288 
    289 		// name int32
    290 		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name))))
    291 
    292 		// args int32
    293 		// TODO: Move into funcinfo.
    294 		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args)))
    295 
    296 		// frame int32
    297 		// This has been removed (it was never set quite correctly anyway).
    298 		// Nothing should use it.
    299 		// Leave an obviously incorrect value.
    300 		// TODO: Remove entirely.
    301 		off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567))
    302 
    303 		if pcln != &pclntab_zpcln {
    304 			renumberfiles(Ctxt, pcln.File, &pcln.Pcfile)
    305 			if false {
    306 				// Sanity check the new numbering
    307 				for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
    308 					if it.value < 1 || it.value > Ctxt.Nhistfile {
    309 						Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile)
    310 						errorexit()
    311 					}
    312 				}
    313 			}
    314 		}
    315 
    316 		// pcdata
    317 		off = addpctab(ftab, off, &pcln.Pcsp)
    318 
    319 		off = addpctab(ftab, off, &pcln.Pcfile)
    320 		off = addpctab(ftab, off, &pcln.Pcline)
    321 		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata)))
    322 		off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata)))
    323 		for i = 0; i < int32(pcln.Npcdata); i++ {
    324 			off = addpctab(ftab, off, &pcln.Pcdata[i])
    325 		}
    326 
    327 		// funcdata, must be pointer-aligned and we're only int32-aligned.
    328 		// Missing funcdata will be 0 (nil pointer).
    329 		if pcln.Nfuncdata > 0 {
    330 			if off&int32(Thearch.Ptrsize-1) != 0 {
    331 				off += 4
    332 			}
    333 			for i = 0; i < int32(pcln.Nfuncdata); i++ {
    334 				if pcln.Funcdata[i] == nil {
    335 					setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize))
    336 				} else {
    337 					// TODO: Dedup.
    338 					funcdata_bytes += pcln.Funcdata[i].Size
    339 
    340 					setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
    341 				}
    342 			}
    343 
    344 			off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize)
    345 		}
    346 
    347 		if off != end {
    348 			Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize)
    349 			errorexit()
    350 		}
    351 
    352 		nfunc++
    353 	}
    354 
    355 	pclntabLastFunc = last
    356 	// Final entry of table is just end pc.
    357 	setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size)
    358 
    359 	// Start file table.
    360 	start := int32(len(ftab.P))
    361 
    362 	start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
    363 	pclntabFiletabOffset = start
    364 	setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start))
    365 
    366 	Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4)
    367 	setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile))
    368 	for s := Ctxt.Filesyms; s != nil; s = s.Next {
    369 		setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
    370 	}
    371 
    372 	ftab.Size = int64(len(ftab.P))
    373 
    374 	if Debug['v'] != 0 {
    375 		fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes))
    376 	}
    377 }
    378 
    379 const (
    380 	BUCKETSIZE    = 256 * MINFUNC
    381 	SUBBUCKETS    = 16
    382 	SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
    383 	NOIDX         = 0x7fffffff
    384 )
    385 
    386 // findfunctab generates a lookup table to quickly find the containing
    387 // function for a pc.  See src/runtime/symtab.go:findfunc for details.
    388 func findfunctab() {
    389 	t := Linklookup(Ctxt, "runtime.findfunctab", 0)
    390 	t.Type = obj.SRODATA
    391 	t.Reachable = true
    392 	t.Local = true
    393 
    394 	// find min and max address
    395 	min := Ctxt.Textp.Value
    396 
    397 	max := int64(0)
    398 	for s := Ctxt.Textp; s != nil; s = s.Next {
    399 		max = s.Value + s.Size
    400 	}
    401 
    402 	// for each subbucket, compute the minimum of all symbol indexes
    403 	// that map to that subbucket.
    404 	n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
    405 
    406 	indexes := make([]int32, n)
    407 	for i := int32(0); i < n; i++ {
    408 		indexes[i] = NOIDX
    409 	}
    410 	idx := int32(0)
    411 	var e *LSym
    412 	var i int32
    413 	var p int64
    414 	var q int64
    415 	for s := Ctxt.Textp; s != nil; s = s.Next {
    416 		if container(s) != 0 {
    417 			continue
    418 		}
    419 		p = s.Value
    420 		e = s.Next
    421 		for container(e) != 0 {
    422 			e = e.Next
    423 		}
    424 		if e != nil {
    425 			q = e.Value
    426 		} else {
    427 			q = max
    428 		}
    429 
    430 		//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
    431 		for ; p < q; p += SUBBUCKETSIZE {
    432 			i = int32((p - min) / SUBBUCKETSIZE)
    433 			if indexes[i] > idx {
    434 				indexes[i] = idx
    435 			}
    436 		}
    437 
    438 		i = int32((q - 1 - min) / SUBBUCKETSIZE)
    439 		if indexes[i] > idx {
    440 			indexes[i] = idx
    441 		}
    442 		idx++
    443 	}
    444 
    445 	// allocate table
    446 	nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
    447 
    448 	Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n))
    449 
    450 	// fill in table
    451 	var base int32
    452 	var j int32
    453 	for i := int32(0); i < nbuckets; i++ {
    454 		base = indexes[i*SUBBUCKETS]
    455 		if base == NOIDX {
    456 			Diag("hole in findfunctab")
    457 		}
    458 		setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
    459 		for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
    460 			idx = indexes[i*SUBBUCKETS+j]
    461 			if idx == NOIDX {
    462 				Diag("hole in findfunctab")
    463 			}
    464 			if idx-base >= 256 {
    465 				Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
    466 			}
    467 
    468 			setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
    469 		}
    470 	}
    471 }
    472