github.com/aloncn/graphics-go@v0.0.1/src/runtime/symtab.go (about) 1 // Copyright 2014 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 runtime 6 7 import ( 8 "runtime/internal/sys" 9 "unsafe" 10 ) 11 12 // NOTE: Func does not expose the actual unexported fields, because we return *Func 13 // values to users, and we want to keep them from being able to overwrite the data 14 // with (say) *f = Func{}. 15 // All code operating on a *Func must call raw to get the *_func instead. 16 17 // A Func represents a Go function in the running binary. 18 type Func struct { 19 opaque struct{} // unexported field to disallow conversions 20 } 21 22 func (f *Func) raw() *_func { 23 return (*_func)(unsafe.Pointer(f)) 24 } 25 26 // funcdata.h 27 const ( 28 _PCDATA_StackMapIndex = 0 29 _FUNCDATA_ArgsPointerMaps = 0 30 _FUNCDATA_LocalsPointerMaps = 1 31 _ArgsSizeUnknown = -0x80000000 32 ) 33 34 // moduledata records information about the layout of the executable 35 // image. It is written by the linker. Any changes here must be 36 // matched changes to the code in cmd/internal/ld/symtab.go:symtab. 37 // moduledata is stored in read-only memory; none of the pointers here 38 // are visible to the garbage collector. 39 type moduledata struct { 40 pclntable []byte 41 ftab []functab 42 filetab []uint32 43 findfunctab uintptr 44 minpc, maxpc uintptr 45 46 text, etext uintptr 47 noptrdata, enoptrdata uintptr 48 data, edata uintptr 49 bss, ebss uintptr 50 noptrbss, enoptrbss uintptr 51 end, gcdata, gcbss uintptr 52 53 typelinks []*_type 54 55 modulename string 56 modulehashes []modulehash 57 58 gcdatamask, gcbssmask bitvector 59 60 next *moduledata 61 } 62 63 // For each shared library a module links against, the linker creates an entry in the 64 // moduledata.modulehashes slice containing the name of the module, the abi hash seen 65 // at link time and a pointer to the runtime abi hash. These are checked in 66 // moduledataverify1 below. 67 type modulehash struct { 68 modulename string 69 linktimehash string 70 runtimehash *string 71 } 72 73 var firstmoduledata moduledata // linker symbol 74 var lastmoduledatap *moduledata // linker symbol 75 76 type functab struct { 77 entry uintptr 78 funcoff uintptr 79 } 80 81 const minfunc = 16 // minimum function size 82 const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table 83 84 // findfunctab is an array of these structures. 85 // Each bucket represents 4096 bytes of the text segment. 86 // Each subbucket represents 256 bytes of the text segment. 87 // To find a function given a pc, locate the bucket and subbucket for 88 // that pc. Add together the idx and subbucket value to obtain a 89 // function index. Then scan the functab array starting at that 90 // index to find the target function. 91 // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead. 92 type findfuncbucket struct { 93 idx uint32 94 subbuckets [16]byte 95 } 96 97 func moduledataverify() { 98 for datap := &firstmoduledata; datap != nil; datap = datap.next { 99 moduledataverify1(datap) 100 } 101 } 102 103 const debugPcln = false 104 105 func moduledataverify1(datap *moduledata) { 106 // See golang.org/s/go12symtab for header: 0xfffffffb, 107 // two zero bytes, a byte giving the PC quantum, 108 // and a byte giving the pointer width in bytes. 109 pcln := *(**[8]byte)(unsafe.Pointer(&datap.pclntable)) 110 pcln32 := *(**[2]uint32)(unsafe.Pointer(&datap.pclntable)) 111 if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != sys.PCQuantum || pcln[7] != sys.PtrSize { 112 println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7])) 113 throw("invalid function symbol table\n") 114 } 115 116 // ftab is lookup table for function by program counter. 117 nftab := len(datap.ftab) - 1 118 var pcCache pcvalueCache 119 for i := 0; i < nftab; i++ { 120 // NOTE: ftab[nftab].entry is legal; it is the address beyond the final function. 121 if datap.ftab[i].entry > datap.ftab[i+1].entry { 122 f1 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff])) 123 f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff])) 124 f2name := "end" 125 if i+1 < nftab { 126 f2name = funcname(f2) 127 } 128 println("function symbol table not sorted by program counter:", hex(datap.ftab[i].entry), funcname(f1), ">", hex(datap.ftab[i+1].entry), f2name) 129 for j := 0; j <= i; j++ { 130 print("\t", hex(datap.ftab[j].entry), " ", funcname((*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff]))), "\n") 131 } 132 throw("invalid runtime symbol table") 133 } 134 135 if debugPcln || nftab-i < 5 { 136 // Check a PC near but not at the very end. 137 // The very end might be just padding that is not covered by the tables. 138 // No architecture rounds function entries to more than 16 bytes, 139 // but if one came along we'd need to subtract more here. 140 // But don't use the next PC if it corresponds to a foreign object chunk 141 // (no pcln table, f2.pcln == 0). That chunk might have an alignment 142 // more than 16 bytes. 143 f := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff])) 144 end := f.entry 145 if i+1 < nftab { 146 f2 := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i+1].funcoff])) 147 if f2.pcln != 0 { 148 end = f2.entry - 16 149 if end < f.entry { 150 end = f.entry 151 } 152 } 153 } 154 pcvalue(f, f.pcfile, end, &pcCache, true) 155 pcvalue(f, f.pcln, end, &pcCache, true) 156 pcvalue(f, f.pcsp, end, &pcCache, true) 157 } 158 } 159 160 if datap.minpc != datap.ftab[0].entry || 161 datap.maxpc != datap.ftab[nftab].entry { 162 throw("minpc or maxpc invalid") 163 } 164 165 for _, modulehash := range datap.modulehashes { 166 if modulehash.linktimehash != *modulehash.runtimehash { 167 println("abi mismatch detected between", datap.modulename, "and", modulehash.modulename) 168 throw("abi mismatch") 169 } 170 } 171 } 172 173 // FuncForPC returns a *Func describing the function that contains the 174 // given program counter address, or else nil. 175 func FuncForPC(pc uintptr) *Func { 176 return (*Func)(unsafe.Pointer(findfunc(pc))) 177 } 178 179 // Name returns the name of the function. 180 func (f *Func) Name() string { 181 return funcname(f.raw()) 182 } 183 184 // Entry returns the entry address of the function. 185 func (f *Func) Entry() uintptr { 186 return f.raw().entry 187 } 188 189 // FileLine returns the file name and line number of the 190 // source code corresponding to the program counter pc. 191 // The result will not be accurate if pc is not a program 192 // counter within f. 193 func (f *Func) FileLine(pc uintptr) (file string, line int) { 194 // Pass strict=false here, because anyone can call this function, 195 // and they might just be wrong about targetpc belonging to f. 196 file, line32 := funcline1(f.raw(), pc, false) 197 return file, int(line32) 198 } 199 200 func findmoduledatap(pc uintptr) *moduledata { 201 for datap := &firstmoduledata; datap != nil; datap = datap.next { 202 if datap.minpc <= pc && pc < datap.maxpc { 203 return datap 204 } 205 } 206 return nil 207 } 208 209 func findfunc(pc uintptr) *_func { 210 datap := findmoduledatap(pc) 211 if datap == nil { 212 return nil 213 } 214 const nsub = uintptr(len(findfuncbucket{}.subbuckets)) 215 216 x := pc - datap.minpc 217 b := x / pcbucketsize 218 i := x % pcbucketsize / (pcbucketsize / nsub) 219 220 ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{}))) 221 idx := ffb.idx + uint32(ffb.subbuckets[i]) 222 if pc < datap.ftab[idx].entry { 223 throw("findfunc: bad findfunctab entry") 224 } 225 226 // linear search to find func with pc >= entry. 227 for datap.ftab[idx+1].entry <= pc { 228 idx++ 229 } 230 return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff])) 231 } 232 233 type pcvalueCache struct { 234 entries [16]pcvalueCacheEnt 235 } 236 237 type pcvalueCacheEnt struct { 238 // targetpc and off together are the key of this cache entry. 239 targetpc uintptr 240 off int32 241 // val is the value of this cached pcvalue entry. 242 val int32 243 } 244 245 func pcvalue(f *_func, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 { 246 if off == 0 { 247 return -1 248 } 249 250 // Check the cache. This speeds up walks of deep stacks, which 251 // tend to have the same recursive functions over and over. 252 // 253 // This cache is small enough that full associativity is 254 // cheaper than doing the hashing for a less associative 255 // cache. 256 if cache != nil { 257 for _, ent := range cache.entries { 258 // We check off first because we're more 259 // likely to have multiple entries with 260 // different offsets for the same targetpc 261 // than the other way around, so we'll usually 262 // fail in the first clause. 263 if ent.off == off && ent.targetpc == targetpc { 264 return ent.val 265 } 266 } 267 } 268 269 datap := findmoduledatap(f.entry) // inefficient 270 if datap == nil { 271 if strict && panicking == 0 { 272 print("runtime: no module data for ", hex(f.entry), "\n") 273 throw("no module data") 274 } 275 return -1 276 } 277 p := datap.pclntable[off:] 278 pc := f.entry 279 val := int32(-1) 280 for { 281 var ok bool 282 p, ok = step(p, &pc, &val, pc == f.entry) 283 if !ok { 284 break 285 } 286 if targetpc < pc { 287 // Replace a random entry in the cache. Random 288 // replacement prevents a performance cliff if 289 // a recursive stack's cycle is slightly 290 // larger than the cache. 291 if cache != nil { 292 ci := fastrand1() % uint32(len(cache.entries)) 293 cache.entries[ci] = pcvalueCacheEnt{ 294 targetpc: targetpc, 295 off: off, 296 val: val, 297 } 298 } 299 300 return val 301 } 302 } 303 304 // If there was a table, it should have covered all program counters. 305 // If not, something is wrong. 306 if panicking != 0 || !strict { 307 return -1 308 } 309 310 print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n") 311 312 p = datap.pclntable[off:] 313 pc = f.entry 314 val = -1 315 for { 316 var ok bool 317 p, ok = step(p, &pc, &val, pc == f.entry) 318 if !ok { 319 break 320 } 321 print("\tvalue=", val, " until pc=", hex(pc), "\n") 322 } 323 324 throw("invalid runtime symbol table") 325 return -1 326 } 327 328 func cfuncname(f *_func) *byte { 329 if f == nil || f.nameoff == 0 { 330 return nil 331 } 332 datap := findmoduledatap(f.entry) // inefficient 333 if datap == nil { 334 return nil 335 } 336 return &datap.pclntable[f.nameoff] 337 } 338 339 func funcname(f *_func) string { 340 return gostringnocopy(cfuncname(f)) 341 } 342 343 func funcline1(f *_func, targetpc uintptr, strict bool) (file string, line int32) { 344 datap := findmoduledatap(f.entry) // inefficient 345 if datap == nil { 346 return "?", 0 347 } 348 fileno := int(pcvalue(f, f.pcfile, targetpc, nil, strict)) 349 line = pcvalue(f, f.pcln, targetpc, nil, strict) 350 if fileno == -1 || line == -1 || fileno >= len(datap.filetab) { 351 // print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n") 352 return "?", 0 353 } 354 file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]]) 355 return 356 } 357 358 func funcline(f *_func, targetpc uintptr) (file string, line int32) { 359 return funcline1(f, targetpc, true) 360 } 361 362 func funcspdelta(f *_func, targetpc uintptr, cache *pcvalueCache) int32 { 363 x := pcvalue(f, f.pcsp, targetpc, cache, true) 364 if x&(sys.PtrSize-1) != 0 { 365 print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n") 366 } 367 return x 368 } 369 370 func pcdatavalue(f *_func, table int32, targetpc uintptr, cache *pcvalueCache) int32 { 371 if table < 0 || table >= f.npcdata { 372 return -1 373 } 374 off := *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4)) 375 return pcvalue(f, off, targetpc, cache, true) 376 } 377 378 func funcdata(f *_func, i int32) unsafe.Pointer { 379 if i < 0 || i >= f.nfuncdata { 380 return nil 381 } 382 p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4) 383 if sys.PtrSize == 8 && uintptr(p)&4 != 0 { 384 if uintptr(unsafe.Pointer(f))&4 != 0 { 385 println("runtime: misaligned func", f) 386 } 387 p = add(p, 4) 388 } 389 return *(*unsafe.Pointer)(add(p, uintptr(i)*sys.PtrSize)) 390 } 391 392 // step advances to the next pc, value pair in the encoded table. 393 func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) { 394 p, uvdelta := readvarint(p) 395 if uvdelta == 0 && !first { 396 return nil, false 397 } 398 if uvdelta&1 != 0 { 399 uvdelta = ^(uvdelta >> 1) 400 } else { 401 uvdelta >>= 1 402 } 403 vdelta := int32(uvdelta) 404 p, pcdelta := readvarint(p) 405 *pc += uintptr(pcdelta * sys.PCQuantum) 406 *val += vdelta 407 return p, true 408 } 409 410 // readvarint reads a varint from p. 411 func readvarint(p []byte) (newp []byte, val uint32) { 412 var v, shift uint32 413 for { 414 b := p[0] 415 p = p[1:] 416 v |= (uint32(b) & 0x7F) << shift 417 if b&0x80 == 0 { 418 break 419 } 420 shift += 7 421 } 422 return p, v 423 } 424 425 type stackmap struct { 426 n int32 // number of bitmaps 427 nbit int32 // number of bits in each bitmap 428 bytedata [1]byte // bitmaps, each starting on a 32-bit boundary 429 } 430 431 //go:nowritebarrier 432 func stackmapdata(stkmap *stackmap, n int32) bitvector { 433 if n < 0 || n >= stkmap.n { 434 throw("stackmapdata: index out of range") 435 } 436 return bitvector{stkmap.nbit, (*byte)(add(unsafe.Pointer(&stkmap.bytedata), uintptr(n*((stkmap.nbit+31)/32*4))))} 437 }