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