github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/internal/ld/pcln.go (about) 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 start := int32(len(ftab.P)) 126 Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P))) 127 copy(ftab.P[start:], d.P) 128 129 return int32(setuint32(Ctxt, ftab, int64(off), uint32(start))) 130 } 131 132 func ftabaddstring(ftab *LSym, s string) int32 { 133 n := int32(len(s)) + 1 134 start := int32(len(ftab.P)) 135 Symgrow(Ctxt, ftab, int64(start)+int64(n)+1) 136 copy(ftab.P[start:], s) 137 return start 138 } 139 140 func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) { 141 var f *LSym 142 143 // Give files numbers. 144 for i := 0; i < len(files); i++ { 145 f = files[i] 146 if f.Type != SFILEPATH { 147 ctxt.Nhistfile++ 148 f.Value = int64(ctxt.Nhistfile) 149 f.Type = SFILEPATH 150 f.Next = ctxt.Filesyms 151 ctxt.Filesyms = f 152 } 153 } 154 155 newval := int32(-1) 156 var out Pcdata 157 158 var dv int32 159 var it Pciter 160 var oldval int32 161 var v uint32 162 var val int32 163 for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) { 164 // value delta 165 oldval = it.value 166 167 if oldval == -1 { 168 val = -1 169 } else { 170 if oldval < 0 || oldval >= int32(len(files)) { 171 log.Fatalf("bad pcdata %d", oldval) 172 } 173 val = int32(files[oldval].Value) 174 } 175 176 dv = val - newval 177 newval = val 178 v = (uint32(dv) << 1) ^ uint32(int32(dv>>31)) 179 addvarint(&out, v) 180 181 // pc delta 182 addvarint(&out, (it.nextpc-it.pc)/it.pcscale) 183 } 184 185 // terminating value delta 186 addvarint(&out, 0) 187 188 *d = out 189 } 190 191 func container(s *LSym) int { 192 // We want to generate func table entries only for the "lowest level" symbols, 193 // not containers of subsymbols. 194 if s != nil && s.Sub != nil { 195 return 1 196 } 197 return 0 198 } 199 200 // pclntab initializes the pclntab symbol with 201 // runtime function and file name information. 202 203 var pclntab_zpcln Pcln 204 205 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. 206 var pclntabNfunc int32 207 var pclntabFiletabOffset int32 208 var pclntabPclntabOffset int32 209 var pclntabFirstFunc *LSym 210 var pclntabLastFunc *LSym 211 212 func pclntab() { 213 funcdata_bytes := int64(0) 214 ftab := Linklookup(Ctxt, "runtime.pclntab", 0) 215 ftab.Type = SPCLNTAB 216 ftab.Reachable = true 217 218 // See golang.org/s/go12symtab for the format. Briefly: 219 // 8-byte header 220 // nfunc [thearch.ptrsize bytes] 221 // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] 222 // end PC [thearch.ptrsize bytes] 223 // offset to file table [4 bytes] 224 nfunc := int32(0) 225 226 for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { 227 if container(Ctxt.Cursym) == 0 { 228 nfunc++ 229 } 230 } 231 232 pclntabNfunc = nfunc 233 Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) 234 setuint32(Ctxt, ftab, 0, 0xfffffffb) 235 setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) 236 setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) 237 setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) 238 pclntabPclntabOffset = int32(8 + Thearch.Ptrsize) 239 240 nfunc = 0 241 var last *LSym 242 var end int32 243 var funcstart int32 244 var i int32 245 var it Pciter 246 var off int32 247 var pcln *Pcln 248 for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { 249 last = Ctxt.Cursym 250 if container(Ctxt.Cursym) != 0 { 251 continue 252 } 253 pcln = Ctxt.Cursym.Pcln 254 if pcln == nil { 255 pcln = &pclntab_zpcln 256 } 257 258 if pclntabFirstFunc == nil { 259 pclntabFirstFunc = Ctxt.Cursym 260 } 261 262 funcstart = int32(len(ftab.P)) 263 funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) 264 265 setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) 266 setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) 267 268 // fixed size of struct, checked below 269 off = funcstart 270 271 end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize) 272 if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { 273 end += 4 274 } 275 Symgrow(Ctxt, ftab, int64(end)) 276 277 // entry uintptr 278 off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym)) 279 280 // name int32 281 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name)))) 282 283 // args int32 284 // TODO: Move into funcinfo. 285 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) 286 287 // frame int32 288 // This has been removed (it was never set quite correctly anyway). 289 // Nothing should use it. 290 // Leave an obviously incorrect value. 291 // TODO: Remove entirely. 292 off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567)) 293 294 if pcln != &pclntab_zpcln { 295 renumberfiles(Ctxt, pcln.File, &pcln.Pcfile) 296 if false { 297 // Sanity check the new numbering 298 for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) { 299 if it.value < 1 || it.value > Ctxt.Nhistfile { 300 Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile) 301 Errorexit() 302 } 303 } 304 } 305 } 306 307 // pcdata 308 off = addpctab(ftab, off, &pcln.Pcsp) 309 310 off = addpctab(ftab, off, &pcln.Pcfile) 311 off = addpctab(ftab, off, &pcln.Pcline) 312 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata))) 313 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata))) 314 for i = 0; i < int32(pcln.Npcdata); i++ { 315 off = addpctab(ftab, off, &pcln.Pcdata[i]) 316 } 317 318 // funcdata, must be pointer-aligned and we're only int32-aligned. 319 // Missing funcdata will be 0 (nil pointer). 320 if pcln.Nfuncdata > 0 { 321 if off&int32(Thearch.Ptrsize-1) != 0 { 322 off += 4 323 } 324 for i = 0; i < int32(pcln.Nfuncdata); i++ { 325 if pcln.Funcdata[i] == nil { 326 setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) 327 } else { 328 // TODO: Dedup. 329 funcdata_bytes += pcln.Funcdata[i].Size 330 331 setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) 332 } 333 } 334 335 off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize) 336 } 337 338 if off != end { 339 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) 340 Errorexit() 341 } 342 343 nfunc++ 344 } 345 346 pclntabLastFunc = last 347 // Final entry of table is just end pc. 348 setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) 349 350 // Start file table. 351 start := int32(len(ftab.P)) 352 353 start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) 354 pclntabFiletabOffset = start 355 setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) 356 357 Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) 358 setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) 359 for s := Ctxt.Filesyms; s != nil; s = s.Next { 360 setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) 361 } 362 363 ftab.Size = int64(len(ftab.P)) 364 365 if Debug['v'] != 0 { 366 fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) 367 } 368 } 369 370 const ( 371 BUCKETSIZE = 256 * MINFUNC 372 SUBBUCKETS = 16 373 SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS 374 NOIDX = 0x7fffffff 375 ) 376 377 // findfunctab generates a lookup table to quickly find the containing 378 // function for a pc. See src/runtime/symtab.go:findfunc for details. 379 func findfunctab() { 380 t := Linklookup(Ctxt, "runtime.findfunctab", 0) 381 t.Type = SRODATA 382 t.Reachable = true 383 t.Local = true 384 385 // find min and max address 386 min := Ctxt.Textp.Value 387 388 max := int64(0) 389 for s := Ctxt.Textp; s != nil; s = s.Next { 390 max = s.Value + s.Size 391 } 392 393 // for each subbucket, compute the minimum of all symbol indexes 394 // that map to that subbucket. 395 n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) 396 397 indexes := make([]int32, n) 398 for i := int32(0); i < n; i++ { 399 indexes[i] = NOIDX 400 } 401 idx := int32(0) 402 var e *LSym 403 var i int32 404 var p int64 405 var q int64 406 for s := Ctxt.Textp; s != nil; s = s.Next { 407 if container(s) != 0 { 408 continue 409 } 410 p = s.Value 411 e = s.Next 412 for container(e) != 0 { 413 e = e.Next 414 } 415 if e != nil { 416 q = e.Value 417 } else { 418 q = max 419 } 420 421 //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); 422 for ; p < q; p += SUBBUCKETSIZE { 423 i = int32((p - min) / SUBBUCKETSIZE) 424 if indexes[i] > idx { 425 indexes[i] = idx 426 } 427 } 428 429 i = int32((q - 1 - min) / SUBBUCKETSIZE) 430 if indexes[i] > idx { 431 indexes[i] = idx 432 } 433 idx++ 434 } 435 436 // allocate table 437 nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) 438 439 Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n)) 440 441 // fill in table 442 var base int32 443 var j int32 444 for i := int32(0); i < nbuckets; i++ { 445 base = indexes[i*SUBBUCKETS] 446 if base == NOIDX { 447 Diag("hole in findfunctab") 448 } 449 setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base)) 450 for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { 451 idx = indexes[i*SUBBUCKETS+j] 452 if idx == NOIDX { 453 Diag("hole in findfunctab") 454 } 455 if idx-base >= 256 { 456 Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) 457 } 458 459 setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) 460 } 461 } 462 }