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