diff options
Diffstat (limited to 'src/pkg/runtime/symtab.go')
| -rw-r--r-- | src/pkg/runtime/symtab.go | 272 |
1 files changed, 209 insertions, 63 deletions
diff --git a/src/pkg/runtime/symtab.go b/src/pkg/runtime/symtab.go index 880a78d481..bd9e9924c4 100644 --- a/src/pkg/runtime/symtab.go +++ b/src/pkg/runtime/symtab.go @@ -6,45 +6,110 @@ package runtime import "unsafe" -// FuncForPC returns a *Func describing the function that contains the -// given program counter address, or else nil. -func FuncForPC(pc uintptr) *Func { - if len(ftabs) == 0 { - return nil - } +// NOTE: Func does not expose the actual unexported fields, because we return *Func +// values to users, and we want to keep them from being able to overwrite the data +// with (say) *f = Func{}. +// All code operating on a *Func must call raw to get the *_func instead. - if pc < ftabs[0].entry || pc >= ftabs[len(ftabs)-1].entry { - return nil +// A Func represents a Go function in the running binary. +type Func struct { + opaque struct{} // unexported field to disallow conversions +} + +func (f *Func) raw() *_func { + return (*_func)(unsafe.Pointer(f)) +} + +// funcdata.h +const ( + _PCDATA_ArgSize = 0 + _PCDATA_StackMapIndex = 1 + _FUNCDATA_ArgsPointerMaps = 0 + _FUNCDATA_LocalsPointerMaps = 1 + _FUNCDATA_DeadValueMaps = 2 + _ArgsSizeUnknown = -0x80000000 +) + +var ( + pclntable []byte + ftab []functab + filetab []uint32 + + pclntab, epclntab struct{} // linker symbols +) + +type functab struct { + entry uintptr + funcoff uintptr +} + +func symtabinit() { + // See golang.org/s/go12symtab for header: 0xfffffffb, + // two zero bytes, a byte giving the PC quantum, + // and a byte giving the pointer width in bytes. + pcln := (*[8]byte)(unsafe.Pointer(&pclntab)) + pcln32 := (*[2]uint32)(unsafe.Pointer(&pclntab)) + if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != _PCQuantum || pcln[7] != ptrSize { + println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7])) + gothrow("invalid function symbol table\n") } - // binary search to find func with entry <= pc. - lo := 0 - nf := len(ftabs) - 1 // last entry is sentinel - for nf > 0 { - n := nf / 2 - f := &ftabs[lo+n] - if f.entry <= pc && pc < ftabs[lo+n+1].entry { - return (*Func)(unsafe.Pointer(&pclntable[f.funcoff])) - } else if pc < f.entry { - nf = n - } else { - lo += n + 1 - nf -= n + 1 + // pclntable is all bytes of pclntab symbol. + sp := (*sliceStruct)(unsafe.Pointer(&pclntable)) + sp.array = unsafe.Pointer(&pclntab) + sp.len = int(uintptr(unsafe.Pointer(&epclntab)) - uintptr(unsafe.Pointer(&pclntab))) + sp.cap = sp.len + + // ftab is lookup table for function by program counter. + nftab := int(*(*uintptr)(add(unsafe.Pointer(pcln), 8))) + p := add(unsafe.Pointer(pcln), 8+ptrSize) + sp = (*sliceStruct)(unsafe.Pointer(&ftab)) + sp.array = p + sp.len = nftab + 1 + sp.cap = sp.len + for i := 0; i < nftab; i++ { + // NOTE: ftab[nftab].entry is legal; it is the address beyond the final function. + if ftab[i].entry > ftab[i+1].entry { + f1 := (*_func)(unsafe.Pointer(&pclntable[ftab[i].funcoff])) + f2 := (*_func)(unsafe.Pointer(&pclntable[ftab[i+1].funcoff])) + f2name := "end" + if i+1 < nftab { + f2name = gofuncname(f2) + } + println("function symbol table not sorted by program counter:", hex(ftab[i].entry), gofuncname(f1), ">", hex(ftab[i+1].entry), f2name) + for j := 0; j <= i; j++ { + print("\t", hex(ftab[j].entry), " ", gofuncname((*_func)(unsafe.Pointer(&pclntable[ftab[j].funcoff])))) + } + gothrow("invalid runtime symbol table") } } - gothrow("FuncForPC: binary search failed") - return nil + // file table follows ftab. + sp = (*sliceStruct)(unsafe.Pointer(&filetab)) + p = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff)) + sp.array = unsafe.Pointer(add(unsafe.Pointer(pcln), ftab[nftab].funcoff)) + // length is in first element of array. + // set len to 1 so we can get first element. + sp.len = 1 + sp.cap = 1 + sp.len = int(filetab[0]) + sp.cap = sp.len +} + +// FuncForPC returns a *Func describing the function that contains the +// given program counter address, or else nil. +func FuncForPC(pc uintptr) *Func { + return (*Func)(unsafe.Pointer(findfunc(pc))) } // Name returns the name of the function. func (f *Func) Name() string { - return cstringToGo(unsafe.Pointer(&pclntable[f.nameoff])) + return gofuncname(f.raw()) } // Entry returns the entry address of the function. func (f *Func) Entry() uintptr { - return f.entry + return f.raw().entry } // FileLine returns the file name and line number of the @@ -52,20 +117,42 @@ func (f *Func) Entry() uintptr { // The result will not be accurate if pc is not a program // counter within f. func (f *Func) FileLine(pc uintptr) (file string, line int) { - fileno := int(f.pcvalue(f.pcfile, pc)) - if fileno == -1 || fileno >= len(filetab) { - return "?", 0 + // Pass strict=false here, because anyone can call this function, + // and they might just be wrong about targetpc belonging to f. + line = int(funcline1(f.raw(), pc, &file, false)) + return file, line +} + +func findfunc(pc uintptr) *_func { + if len(ftab) == 0 { + return nil } - line = int(f.pcvalue(f.pcln, pc)) - if line == -1 { - return "?", 0 + + if pc < ftab[0].entry || pc >= ftab[len(ftab)-1].entry { + return nil } - file = cstringToGo(unsafe.Pointer(&pclntable[filetab[fileno]])) - return file, line + + // binary search to find func with entry <= pc. + lo := 0 + nf := len(ftab) - 1 // last entry is sentinel + for nf > 0 { + n := nf / 2 + f := &ftab[lo+n] + if f.entry <= pc && pc < ftab[lo+n+1].entry { + return (*_func)(unsafe.Pointer(&pclntable[f.funcoff])) + } else if pc < f.entry { + nf = n + } else { + lo += n + 1 + nf -= n + 1 + } + } + + gothrow("findfunc: binary search failed") + return nil } -// Return associated data value for targetpc in func f. -func (f *Func) pcvalue(off int32, targetpc uintptr) int32 { +func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 { if off == 0 { return -1 } @@ -82,9 +169,95 @@ func (f *Func) pcvalue(off int32, targetpc uintptr) int32 { return val } } + + // If there was a table, it should have covered all program counters. + // If not, something is wrong. + if panicking != 0 || !strict { + return -1 + } + + print("runtime: invalid pc-encoded table f=", gofuncname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n") + + p = pclntable[off:] + pc = f.entry + val = -1 + for { + var ok bool + p, ok = step(p, &pc, &val, pc == f.entry) + if !ok { + break + } + print("\tvalue=", val, " until pc=", hex(pc), "\n") + } + + gothrow("invalid runtime symbol table") return -1 } +func funcname(f *_func) *byte { + if f == nil || f.nameoff == 0 { + return nil + } + return (*byte)(unsafe.Pointer(&pclntable[f.nameoff])) +} + +func gofuncname(f *_func) string { + return gostringnocopy(funcname(f)) +} + +func funcline1(f *_func, targetpc uintptr, file *string, strict bool) int32 { + *file = "?" + fileno := int(pcvalue(f, f.pcfile, targetpc, strict)) + line := pcvalue(f, f.pcln, targetpc, strict) + if fileno == -1 || line == -1 || fileno >= len(filetab) { + // print("looking for ", hex(targetpc), " in ", gofuncname(f), " got file=", fileno, " line=", lineno, "\n") + return 0 + } + *file = gostringnocopy(&pclntable[filetab[fileno]]) + return line +} + +func funcline(f *_func, targetpc uintptr, file *string) int32 { + return funcline1(f, targetpc, file, true) +} + +func funcspdelta(f *_func, targetpc uintptr) int32 { + x := pcvalue(f, f.pcsp, targetpc, true) + if x&(ptrSize-1) != 0 { + print("invalid spdelta ", f.pcsp, " ", x, "\n") + } + return x +} + +func pcdatavalue(f *_func, table int32, targetpc uintptr) int32 { + if table < 0 || table >= f.npcdata { + return -1 + } + off := *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4)) + return pcvalue(f, off, targetpc, true) +} + +func funcarglen(f *_func, targetpc uintptr) int32 { + if targetpc == f.entry { + return 0 + } + return pcdatavalue(f, _PCDATA_ArgSize, targetpc-_PCQuantum) +} + +func funcdata(f *_func, i int32) unsafe.Pointer { + if i < 0 || i >= f.nfuncdata { + return nil + } + p := add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(f.npcdata)*4) + if ptrSize == 8 && uintptr(p)&4 != 0 { + if uintptr(unsafe.Pointer(f))&4 != 0 { + println("runtime: misaligned func", f) + } + p = add(p, 4) + } + return *(*unsafe.Pointer)(add(p, uintptr(i)*ptrSize)) +} + // step advances to the next pc, value pair in the encoded table. func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) { p, uvdelta := readvarint(p) @@ -98,7 +271,7 @@ func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) } vdelta := int32(uvdelta) p, pcdelta := readvarint(p) - *pc += uintptr(pcdelta * pcquantum) + *pc += uintptr(pcdelta * _PCQuantum) *val += vdelta return p, true } @@ -117,30 +290,3 @@ func readvarint(p []byte) (newp []byte, val uint32) { } return p, v } - -// Populated by runtime·symtabinit during bootstrapping. Treat as immutable. -var ( - pclntable []byte - ftabs []ftab - filetab []uint32 - pcquantum uint32 -) - -type Func struct { - entry uintptr // start pc - nameoff int32 // function name - - args int32 // in/out args size - frame int32 // legacy frame size; use pcsp if possible - - pcsp int32 - pcfile int32 - pcln int32 - npcdata int32 - nfuncdata int32 -} - -type ftab struct { - entry uintptr - funcoff uintptr -} |
