diff options
Diffstat (limited to 'src/runtime/symtab.go')
-rw-r--r-- | src/runtime/symtab.go | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go new file mode 100644 index 000000000..45d107b77 --- /dev/null +++ b/src/runtime/symtab.go @@ -0,0 +1,288 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import "unsafe" + +// 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. + +// 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") + } + + // 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") + } + } + + // The ftab ends with a half functab consisting only of + // 'entry', followed by a uint32 giving the pcln-relative + // offset of the file table. + sp = (*sliceStruct)(unsafe.Pointer(&filetab)) + end := unsafe.Pointer(&ftab[nftab].funcoff) // just beyond ftab + fileoffset := *(*uint32)(end) + sp.array = unsafe.Pointer(&pclntable[fileoffset]) + // 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 gofuncname(f.raw()) +} + +// Entry returns the entry address of the function. +func (f *Func) Entry() uintptr { + return f.raw().entry +} + +// FileLine returns the file name and line number of the +// source code corresponding to the program counter pc. +// 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) { + // 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 + } + + if pc < ftab[0].entry || pc >= ftab[len(ftab)-1].entry { + return nil + } + + // 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 +} + +func pcvalue(f *_func, off int32, targetpc uintptr, strict bool) int32 { + if off == 0 { + return -1 + } + p := pclntable[off:] + pc := f.entry + val := int32(-1) + for { + var ok bool + p, ok = step(p, &pc, &val, pc == f.entry) + if !ok { + break + } + if targetpc < pc { + 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 ", hex(f.entry), " ", hex(targetpc), " ", hex(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 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) + if uvdelta == 0 && !first { + return nil, false + } + if uvdelta&1 != 0 { + uvdelta = ^(uvdelta >> 1) + } else { + uvdelta >>= 1 + } + vdelta := int32(uvdelta) + p, pcdelta := readvarint(p) + *pc += uintptr(pcdelta * _PCQuantum) + *val += vdelta + return p, true +} + +// readvarint reads a varint from p. +func readvarint(p []byte) (newp []byte, val uint32) { + var v, shift uint32 + for { + b := p[0] + p = p[1:] + v |= (uint32(b) & 0x7F) << shift + if b&0x80 == 0 { + break + } + shift += 7 + } + return p, v +} |