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