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