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