Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 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 | |
Michael Matloob | 432cb66 | 2015-11-11 12:39:30 -0500 | [diff] [blame] | 7 | import ( |
| 8 | "runtime/internal/sys" |
| 9 | "unsafe" |
| 10 | ) |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 11 | |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 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 ( |
Russ Cox | d98553a | 2014-11-11 17:04:34 -0500 | [diff] [blame] | 28 | _PCDATA_StackMapIndex = 0 |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 29 | _FUNCDATA_ArgsPointerMaps = 0 |
| 30 | _FUNCDATA_LocalsPointerMaps = 1 |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 31 | _ArgsSizeUnknown = -0x80000000 |
| 32 | ) |
| 33 | |
Michael Hudson-Doyle | 67426a8 | 2015-03-12 12:22:18 +1300 | [diff] [blame] | 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. |
Russ Cox | 512f75e | 2015-05-08 01:43:18 -0400 | [diff] [blame] | 37 | // moduledata is stored in read-only memory; none of the pointers here |
| 38 | // are visible to the garbage collector. |
Michael Hudson-Doyle | 67426a8 | 2015-03-12 12:22:18 +1300 | [diff] [blame] | 39 | type moduledata struct { |
Michael Hudson-Doyle | 3a84e33 | 2015-03-16 11:53:08 +1300 | [diff] [blame] | 40 | pclntable []byte |
| 41 | ftab []functab |
| 42 | filetab []uint32 |
| 43 | findfunctab uintptr |
| 44 | minpc, maxpc uintptr |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 45 | |
Michael Hudson-Doyle | 67426a8 | 2015-03-12 12:22:18 +1300 | [diff] [blame] | 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 |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 52 | |
Michael Hudson-Doyle | 3a84e33 | 2015-03-16 11:53:08 +1300 | [diff] [blame] | 53 | typelinks []*_type |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 54 | |
Michael Hudson-Doyle | 77fc03f | 2015-04-11 12:05:21 +0800 | [diff] [blame] | 55 | modulename string |
| 56 | modulehashes []modulehash |
| 57 | |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 58 | gcdatamask, gcbssmask bitvector |
| 59 | |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 60 | next *moduledata |
Michael Hudson-Doyle | 67426a8 | 2015-03-12 12:22:18 +1300 | [diff] [blame] | 61 | } |
| 62 | |
Michael Hudson-Doyle | 77fc03f | 2015-04-11 12:05:21 +0800 | [diff] [blame] | 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 | |
Michael Hudson-Doyle | f616af2 | 2015-04-01 14:17:43 +1300 | [diff] [blame] | 73 | var firstmoduledata moduledata // linker symbol |
| 74 | var lastmoduledatap *moduledata // linker symbol |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 75 | |
| 76 | type functab struct { |
| 77 | entry uintptr |
| 78 | funcoff uintptr |
| 79 | } |
| 80 | |
Keith Randall | 6dd3166 | 2015-02-09 16:36:25 -0800 | [diff] [blame] | 81 | const minfunc = 16 // minimum function size |
| 82 | const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 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 { |
Keith Randall | 6dd3166 | 2015-02-09 16:36:25 -0800 | [diff] [blame] | 93 | idx uint32 |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 94 | subbuckets [16]byte |
| 95 | } |
| 96 | |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 97 | func moduledataverify() { |
| 98 | for datap := &firstmoduledata; datap != nil; datap = datap.next { |
| 99 | moduledataverify1(datap) |
| 100 | } |
| 101 | } |
| 102 | |
Russ Cox | 434e0bc | 2015-06-28 22:26:35 -0400 | [diff] [blame] | 103 | const debugPcln = false |
| 104 | |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 105 | func moduledataverify1(datap *moduledata) { |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 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. |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 109 | pcln := *(**[8]byte)(unsafe.Pointer(&datap.pclntable)) |
| 110 | pcln32 := *(**[2]uint32)(unsafe.Pointer(&datap.pclntable)) |
Michael Matloob | 432cb66 | 2015-11-11 12:39:30 -0500 | [diff] [blame] | 111 | if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != sys.PCQuantum || pcln[7] != sys.PtrSize { |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 112 | println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7])) |
Keith Randall | b2a950b | 2014-12-27 20:58:00 -0800 | [diff] [blame] | 113 | throw("invalid function symbol table\n") |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 114 | } |
| 115 | |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 116 | // ftab is lookup table for function by program counter. |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 117 | nftab := len(datap.ftab) - 1 |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 118 | var pcCache pcvalueCache |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 119 | for i := 0; i < nftab; i++ { |
| 120 | // NOTE: ftab[nftab].entry is legal; it is the address beyond the final function. |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 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])) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 124 | f2name := "end" |
| 125 | if i+1 < nftab { |
Keith Randall | 0bb8fc6 | 2014-12-28 23:16:32 -0800 | [diff] [blame] | 126 | f2name = funcname(f2) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 127 | } |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 128 | println("function symbol table not sorted by program counter:", hex(datap.ftab[i].entry), funcname(f1), ">", hex(datap.ftab[i+1].entry), f2name) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 129 | for j := 0; j <= i; j++ { |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 130 | print("\t", hex(datap.ftab[j].entry), " ", funcname((*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[j].funcoff]))), "\n") |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 131 | } |
Keith Randall | b2a950b | 2014-12-27 20:58:00 -0800 | [diff] [blame] | 132 | throw("invalid runtime symbol table") |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 133 | } |
Russ Cox | 434e0bc | 2015-06-28 22:26:35 -0400 | [diff] [blame] | 134 | |
| 135 | if debugPcln || nftab-i < 5 { |
Russ Cox | 8b99bb7 | 2015-06-29 13:32:09 -0400 | [diff] [blame] | 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. |
Russ Cox | 74ec5bf | 2015-07-23 02:23:14 -0400 | [diff] [blame] | 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. |
Russ Cox | 434e0bc | 2015-06-28 22:26:35 -0400 | [diff] [blame] | 143 | f := (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[i].funcoff])) |
Russ Cox | 74ec5bf | 2015-07-23 02:23:14 -0400 | [diff] [blame] | 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 | } |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 154 | pcvalue(f, f.pcfile, end, &pcCache, true) |
| 155 | pcvalue(f, f.pcln, end, &pcCache, true) |
| 156 | pcvalue(f, f.pcsp, end, &pcCache, true) |
Russ Cox | 434e0bc | 2015-06-28 22:26:35 -0400 | [diff] [blame] | 157 | } |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 158 | } |
| 159 | |
Michael Hudson-Doyle | fa89673 | 2015-05-06 14:30:28 +1200 | [diff] [blame] | 160 | if datap.minpc != datap.ftab[0].entry || |
| 161 | datap.maxpc != datap.ftab[nftab].entry { |
Michael Hudson-Doyle | 3a84e33 | 2015-03-16 11:53:08 +1300 | [diff] [blame] | 162 | throw("minpc or maxpc invalid") |
| 163 | } |
Michael Hudson-Doyle | 77fc03f | 2015-04-11 12:05:21 +0800 | [diff] [blame] | 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 | } |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 171 | } |
| 172 | |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 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 { |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 176 | return (*Func)(unsafe.Pointer(findfunc(pc))) |
| 177 | } |
| 178 | |
| 179 | // Name returns the name of the function. |
| 180 | func (f *Func) Name() string { |
Keith Randall | 0bb8fc6 | 2014-12-28 23:16:32 -0800 | [diff] [blame] | 181 | return funcname(f.raw()) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 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. |
Russ Cox | 656be31 | 2014-11-12 14:54:31 -0500 | [diff] [blame] | 196 | file, line32 := funcline1(f.raw(), pc, false) |
| 197 | return file, int(line32) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 198 | } |
| 199 | |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 200 | func findmoduledatap(pc uintptr) *moduledata { |
Michael Hudson-Doyle | a1f5759 | 2015-04-07 12:55:02 +1200 | [diff] [blame] | 201 | for datap := &firstmoduledata; datap != nil; datap = datap.next { |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 202 | if datap.minpc <= pc && pc <= datap.maxpc { |
| 203 | return datap |
| 204 | } |
| 205 | } |
| 206 | return nil |
| 207 | } |
| 208 | |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 209 | func findfunc(pc uintptr) *_func { |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 210 | datap := findmoduledatap(pc) |
| 211 | if datap == nil { |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 212 | return nil |
| 213 | } |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 214 | const nsub = uintptr(len(findfuncbucket{}.subbuckets)) |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 215 | |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 216 | x := pc - datap.minpc |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 217 | b := x / pcbucketsize |
Keith Randall | 6dd3166 | 2015-02-09 16:36:25 -0800 | [diff] [blame] | 218 | i := x % pcbucketsize / (pcbucketsize / nsub) |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 219 | |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 220 | ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{}))) |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 221 | idx := ffb.idx + uint32(ffb.subbuckets[i]) |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 222 | if pc < datap.ftab[idx].entry { |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 223 | throw("findfunc: bad findfunctab entry") |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 224 | } |
| 225 | |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 226 | // linear search to find func with pc >= entry. |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 227 | for datap.ftab[idx+1].entry <= pc { |
Keith Randall | 63116de | 2014-12-27 19:26:40 -0800 | [diff] [blame] | 228 | idx++ |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 229 | } |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 230 | return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff])) |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 231 | } |
| 232 | |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 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 { |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 246 | if off == 0 { |
| 247 | return -1 |
| 248 | } |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 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 | |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 269 | datap := findmoduledatap(f.entry) // inefficient |
Ian Lance Taylor | 692054e | 2015-07-18 13:35:12 -0700 | [diff] [blame] | 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 | } |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 277 | p := datap.pclntable[off:] |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 278 | pc := f.entry |
| 279 | val := int32(-1) |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 280 | for { |
| 281 | var ok bool |
| 282 | p, ok = step(p, &pc, &val, pc == f.entry) |
| 283 | if !ok { |
| 284 | break |
| 285 | } |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 286 | if targetpc < pc { |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 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 | |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 300 | return val |
| 301 | } |
| 302 | } |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 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 | |
Keith Randall | 0bb8fc6 | 2014-12-28 23:16:32 -0800 | [diff] [blame] | 310 | print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n") |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 311 | |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 312 | p = datap.pclntable[off:] |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 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 | |
Keith Randall | b2a950b | 2014-12-27 20:58:00 -0800 | [diff] [blame] | 324 | throw("invalid runtime symbol table") |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 325 | return -1 |
| 326 | } |
| 327 | |
Keith Randall | 0bb8fc6 | 2014-12-28 23:16:32 -0800 | [diff] [blame] | 328 | func cfuncname(f *_func) *byte { |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 329 | if f == nil || f.nameoff == 0 { |
| 330 | return nil |
| 331 | } |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 332 | datap := findmoduledatap(f.entry) // inefficient |
Ian Lance Taylor | 692054e | 2015-07-18 13:35:12 -0700 | [diff] [blame] | 333 | if datap == nil { |
| 334 | return nil |
| 335 | } |
Nodir Turakulov | 3be4d59 | 2015-10-09 11:15:53 -0700 | [diff] [blame] | 336 | return &datap.pclntable[f.nameoff] |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 337 | } |
| 338 | |
Keith Randall | 0bb8fc6 | 2014-12-28 23:16:32 -0800 | [diff] [blame] | 339 | func funcname(f *_func) string { |
| 340 | return gostringnocopy(cfuncname(f)) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 341 | } |
| 342 | |
Russ Cox | 656be31 | 2014-11-12 14:54:31 -0500 | [diff] [blame] | 343 | func funcline1(f *_func, targetpc uintptr, strict bool) (file string, line int32) { |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 344 | datap := findmoduledatap(f.entry) // inefficient |
Ian Lance Taylor | 692054e | 2015-07-18 13:35:12 -0700 | [diff] [blame] | 345 | if datap == nil { |
| 346 | return "?", 0 |
| 347 | } |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 348 | fileno := int(pcvalue(f, f.pcfile, targetpc, nil, strict)) |
| 349 | line = pcvalue(f, f.pcln, targetpc, nil, strict) |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 350 | if fileno == -1 || line == -1 || fileno >= len(datap.filetab) { |
Keith Randall | 0bb8fc6 | 2014-12-28 23:16:32 -0800 | [diff] [blame] | 351 | // print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n") |
Russ Cox | 656be31 | 2014-11-12 14:54:31 -0500 | [diff] [blame] | 352 | return "?", 0 |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 353 | } |
Michael Hudson-Doyle | fae4a12 | 2015-03-29 21:59:00 +0000 | [diff] [blame] | 354 | file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]]) |
Russ Cox | 656be31 | 2014-11-12 14:54:31 -0500 | [diff] [blame] | 355 | return |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 356 | } |
| 357 | |
Russ Cox | 656be31 | 2014-11-12 14:54:31 -0500 | [diff] [blame] | 358 | func funcline(f *_func, targetpc uintptr) (file string, line int32) { |
| 359 | return funcline1(f, targetpc, true) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 360 | } |
| 361 | |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 362 | func funcspdelta(f *_func, targetpc uintptr, cache *pcvalueCache) int32 { |
| 363 | x := pcvalue(f, f.pcsp, targetpc, cache, true) |
Michael Matloob | 432cb66 | 2015-11-11 12:39:30 -0500 | [diff] [blame] | 364 | if x&(sys.PtrSize-1) != 0 { |
Russ Cox | 434e0bc | 2015-06-28 22:26:35 -0400 | [diff] [blame] | 365 | print("invalid spdelta ", funcname(f), " ", hex(f.entry), " ", hex(targetpc), " ", hex(f.pcsp), " ", x, "\n") |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 366 | } |
| 367 | return x |
| 368 | } |
| 369 | |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 370 | func pcdatavalue(f *_func, table int32, targetpc uintptr, cache *pcvalueCache) int32 { |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 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)) |
Austin Clements | beedb1e | 2015-08-12 23:43:43 -0400 | [diff] [blame] | 375 | return pcvalue(f, off, targetpc, cache, true) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 376 | } |
| 377 | |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 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) |
Michael Matloob | 432cb66 | 2015-11-11 12:39:30 -0500 | [diff] [blame] | 383 | if sys.PtrSize == 8 && uintptr(p)&4 != 0 { |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 384 | if uintptr(unsafe.Pointer(f))&4 != 0 { |
| 385 | println("runtime: misaligned func", f) |
| 386 | } |
| 387 | p = add(p, 4) |
| 388 | } |
Michael Matloob | 432cb66 | 2015-11-11 12:39:30 -0500 | [diff] [blame] | 389 | return *(*unsafe.Pointer)(add(p, uintptr(i)*sys.PtrSize)) |
Russ Cox | 97f8386 | 2014-09-03 13:02:48 -0400 | [diff] [blame] | 390 | } |
| 391 | |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 392 | // step advances to the next pc, value pair in the encoded table. |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 393 | func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) { |
| 394 | p, uvdelta := readvarint(p) |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 395 | if uvdelta == 0 && !first { |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 396 | return nil, false |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 397 | } |
| 398 | if uvdelta&1 != 0 { |
| 399 | uvdelta = ^(uvdelta >> 1) |
| 400 | } else { |
| 401 | uvdelta >>= 1 |
| 402 | } |
| 403 | vdelta := int32(uvdelta) |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 404 | p, pcdelta := readvarint(p) |
Michael Matloob | 432cb66 | 2015-11-11 12:39:30 -0500 | [diff] [blame] | 405 | *pc += uintptr(pcdelta * sys.PCQuantum) |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 406 | *val += vdelta |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 407 | return p, true |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 408 | } |
| 409 | |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 410 | // readvarint reads a varint from p. |
| 411 | func readvarint(p []byte) (newp []byte, val uint32) { |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 412 | var v, shift uint32 |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 413 | for { |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 414 | b := p[0] |
| 415 | p = p[1:] |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 416 | v |= (uint32(b) & 0x7F) << shift |
| 417 | if b&0x80 == 0 { |
| 418 | break |
| 419 | } |
| 420 | shift += 7 |
| 421 | } |
Russ Cox | 6c67dd9 | 2014-08-24 20:28:29 -0400 | [diff] [blame] | 422 | return p, v |
Josh Bleecher Snyder | 0be5973 | 2014-08-22 08:41:32 -0700 | [diff] [blame] | 423 | } |
Russ Cox | 484f801 | 2015-02-19 13:38:46 -0500 | [diff] [blame] | 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 | } |