]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: convert symtab.c into symtab.go
authorRuss Cox <rsc@golang.org>
Wed, 3 Sep 2014 17:02:48 +0000 (13:02 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 3 Sep 2014 17:02:48 +0000 (13:02 -0400)
Because symtab.c was partially converted before,
the diffs are not terribly useful.

The earlier conversion was trying to refactor or
clean up the code in addition to doing the translation.
It also made a mistake by redefining Func to be something
users could overwrite.

I undid those changes, making symtab.go a more
literal line-for-line translation of symtab.c instead.

LGTM=josharian
R=golang-codereviews, dave, bradfitz, josharian
CC=golang-codereviews, iant, khr, r
https://golang.org/cl/140880043

src/pkg/runtime/error.go
src/pkg/runtime/extern.go
src/pkg/runtime/funcdata.h
src/pkg/runtime/runtime.c
src/pkg/runtime/string.go
src/pkg/runtime/stubs.go
src/pkg/runtime/symtab.c [deleted file]
src/pkg/runtime/symtab.go
src/pkg/runtime/traceback.go

index 54591ee43e4e571a4f1aa4bb06ef8421936dff5a..3ea93680ce971f809e078918f412db946abefc6b 100644 (file)
@@ -85,7 +85,7 @@ type errorCString struct{ cstr unsafe.Pointer }
 func (e errorCString) RuntimeError() {}
 
 func (e errorCString) Error() string {
-       return "runtime error: " + cstringToGo(e.cstr)
+       return "runtime error: " + gostringnocopy((*byte)(e.cstr))
 }
 
 // For calling from C.
index 2ca22d923bcab9549bb226803c79e114af618f13..3d06a23fcea414e5a0f5789fe7610c69bae7f655 100644 (file)
@@ -75,12 +75,6 @@ of the run-time system.
 */
 package runtime
 
-import "unsafe"
-
-// sigpanic is the C function sigpanic.
-// That is, unsafe.Pointer(&sigpanic) is the C function pointer for sigpanic.
-var sigpanic struct{}
-
 // Caller reports file and line number information about function invocations on
 // the calling goroutine's stack.  The argument skip is the number of stack frames
 // to ascend, with 0 identifying the caller of Caller.  (For historical reasons the
@@ -109,7 +103,7 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
        // All architectures turn faults into apparent calls to sigpanic.
        // If we see a call to sigpanic, we do not back up the PC to find
        // the line number of the call instruction, because there is no call.
-       if xpc > f.entry && (g == nil || g.entry != uintptr(unsafe.Pointer(&sigpanic))) {
+       if xpc > f.entry && (g == nil || g.entry != funcPC(sigpanic)) {
                xpc--
        }
        line = int(funcline(f, xpc, &file))
@@ -117,11 +111,6 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) {
        return
 }
 
-func findfunc(uintptr) *_func
-
-//go:noescape
-func funcline(*_func, uintptr, *string) int32
-
 // Callers fills the slice pc with the program counters of function invocations
 // on the calling goroutine's stack.  The argument skip is the number of stack frames
 // to skip before recording in pc, with 0 identifying the frame for Callers itself and
index 85e82838b7d6c391f789ecf4a1966d792de5a422..dc9c41363e4be72298aa6239adee6859a47a9400 100644 (file)
@@ -6,6 +6,8 @@
 // in Go binaries. It is included by both C and assembly, so it must
 // be written using #defines. It is included by the runtime package
 // as well as the compilers.
+//
+// symtab.go also contains a copy of these constants.
 
 #define PCDATA_ArgSize 0 /* argument size at CALL instruction */
 #define PCDATA_StackMapIndex 1
index d4999e74650d542103471554f121048853faf834..b0adfb601b71e1bb0efe9291e495ca07f2bb4a08 100644 (file)
@@ -120,6 +120,7 @@ runtime·goenvs_unix(void)
        syscall·envs.cap = n;
 }
 
+#pragma textflag NOSPLIT
 Slice
 runtime·environ()
 {
index 1cefad9671765f6ce51eec01224cc2180fc90f3d..91f33db118602a501567eaecee8a5da84d58be61 100644 (file)
@@ -145,19 +145,6 @@ type stringStruct struct {
        len int
 }
 
-func cstringToGo(str unsafe.Pointer) (s string) {
-       i := 0
-       for ; ; i++ {
-               if *(*byte)(unsafe.Pointer(uintptr(str) + uintptr(i))) == 0 {
-                       break
-               }
-       }
-       t := (*stringStruct)(unsafe.Pointer(&s))
-       t.str = unsafe.Pointer(str)
-       t.len = i
-       return
-}
-
 func intstring(v int64) string {
        s, b := rawstring(4)
        n := runetochar(b, rune(v))
index bf83500b5a2f33822d4cfc056cd43486a3a30ec0..14857908fd7a074737948b083bc960133d460dfc 100644 (file)
@@ -248,19 +248,15 @@ func open(name *byte, mode, perm int32) int32
 //go:noescape
 func gotraceback(*bool) int32
 
-func funcname(*_func) *byte
-
-func gofuncname(f *_func) string {
-       return gostringnocopy(funcname(f))
-}
-
 const _NoArgs = ^uintptr(0)
 
-var newproc, lessstack struct{} // C/assembly functions
-
-func funcspdelta(*_func, uintptr) int32 // symtab.c
-func funcarglen(*_func, uintptr) int32  // symtab.c
-const _ArgsSizeUnknown = -0x80000000    // funcdata.h
+func newstack()
+func newproc()
+func lessstack()
+func morestack()
+func mstart()
+func rt0_go()
+func sigpanic()
 
 // return0 is a stub used to return 0 from deferproc.
 // It is called at the very end of deferproc to signal
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c
deleted file mode 100644 (file)
index 802ad5d..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright 2009 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.
-
-// Runtime symbol table parsing.
-// See http://golang.org/s/go12symtab for an overview.
-
-#include "runtime.h"
-#include "defs_GOOS_GOARCH.h"
-#include "os_GOOS.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "funcdata.h"
-
-typedef struct Ftab Ftab;
-struct Ftab
-{
-       uintptr entry;
-       uintptr funcoff;
-};
-
-extern byte runtime·pclntab[];
-extern byte runtime·epclntab[];
-
-static Ftab *ftab;
-static uintptr runtime·nftab;
-static uint32 *filetab;
-static uint32 runtime·nfiletab;
-
-extern Slice runtime·pclntable;
-extern Slice runtime·ftabs;
-extern Slice runtime·filetab;
-extern uint32 runtime·pcquantum;
-
-static String end = { (uint8*)"end", 3 };
-
-void
-runtime·symtabinit(void)
-{
-       int32 i, j;
-       Func *f1, *f2;
-       
-       // 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.
-       if(*(uint32*)runtime·pclntab != 0xfffffffb || runtime·pclntab[4] != 0 || runtime·pclntab[5] != 0 || runtime·pclntab[6] != PCQuantum || runtime·pclntab[7] != sizeof(void*)) {
-               runtime·printf("runtime: function symbol table header: %x %x\n", *(uint32*)runtime·pclntab, *(uint32*)(runtime·pclntab+4));
-               runtime·throw("invalid function symbol table\n");
-       }
-
-       runtime·nftab = *(uintptr*)(runtime·pclntab+8);
-       ftab = (Ftab*)(runtime·pclntab+8+sizeof(void*));
-       for(i=0; i<runtime·nftab; i++) {
-               // NOTE: ftab[runtime·nftab].entry is legal; it is the address beyond the final function.
-               if(ftab[i].entry > ftab[i+1].entry) {
-                       f1 = (Func*)(runtime·pclntab + ftab[i].funcoff);
-                       f2 = (Func*)(runtime·pclntab + ftab[i+1].funcoff);
-                       runtime·printf("function symbol table not sorted by program counter: %p %s > %p %s", ftab[i].entry, runtime·funcname(f1), ftab[i+1].entry, i+1 == runtime·nftab ? "end" : runtime·funcname(f2));
-                       for(j=0; j<=i; j++)
-                               runtime·printf("\t%p %s\n", ftab[j].entry, runtime·funcname((Func*)(runtime·pclntab + ftab[j].funcoff)));
-                       runtime·throw("invalid runtime symbol table");
-               }
-       }
-       
-       filetab = (uint32*)(runtime·pclntab + *(uint32*)&ftab[runtime·nftab].funcoff);
-       runtime·nfiletab = filetab[0];
-
-       runtime·pcquantum = PCQuantum;
-
-       runtime·pclntable.array = (byte*)runtime·pclntab;
-       runtime·pclntable.len = (byte*)runtime·epclntab - (byte*)runtime·pclntab;
-       runtime·pclntable.cap = runtime·pclntable.len;
-
-       runtime·ftabs.array = (byte*)ftab;
-       runtime·ftabs.len = runtime·nftab+1;
-       runtime·ftabs.cap = runtime·ftabs.len;
-
-       runtime·filetab.array = (byte*)filetab;
-       runtime·filetab.len = filetab[0];
-       runtime·filetab.cap = runtime·filetab.len;
-}
-
-static uint32
-readvarint(byte **pp)
-{
-       byte *p;
-       uint32 v;
-       int32 shift;
-       
-       v = 0;
-       p = *pp;
-       for(shift = 0;; shift += 7) {
-               v |= (*p & 0x7F) << shift;
-               if(!(*p++ & 0x80))
-                       break;
-       }
-       *pp = p;
-       return v;
-}
-
-void*
-runtime·funcdata(Func *f, int32 i)
-{
-       byte *p;
-
-       if(i < 0 || i >= f->nfuncdata)
-               return nil;
-       p = (byte*)&f->nfuncdata + 4 + f->npcdata*4;
-       if(sizeof(void*) == 8 && ((uintptr)p & 4)) {
-               if(((uintptr)f & 4))
-                       runtime·printf("misaligned func %p\n", f);
-               p += 4;
-       }
-       return ((void**)p)[i];
-}
-
-static bool
-step(byte **pp, uintptr *pc, int32 *value, bool first)
-{
-       uint32 uvdelta, pcdelta;
-       int32 vdelta;
-
-       uvdelta = readvarint(pp);
-       if(uvdelta == 0 && !first)
-               return 0;
-       if(uvdelta&1)
-               uvdelta = ~(uvdelta>>1);
-       else
-               uvdelta >>= 1;
-       vdelta = (int32)uvdelta;
-       pcdelta = readvarint(pp) * PCQuantum;
-       *value += vdelta;
-       *pc += pcdelta;
-       return 1;
-}
-
-// Return associated data value for targetpc in func f.
-// (Source file is f->src.)
-static int32
-pcvalue(Func *f, int32 off, uintptr targetpc, bool strict)
-{
-       byte *p;
-       uintptr pc;
-       int32 value;
-
-       enum {
-               debug = 0
-       };
-
-       // The table is a delta-encoded sequence of (value, pc) pairs.
-       // Each pair states the given value is in effect up to pc.
-       // The value deltas are signed, zig-zag encoded.
-       // The pc deltas are unsigned.
-       // The starting value is -1, the starting pc is the function entry.
-       // The table ends at a value delta of 0 except in the first pair.
-       if(off == 0)
-               return -1;
-       p = runtime·pclntab + off;
-       pc = f->entry;
-       value = -1;
-
-       if(debug && !runtime·panicking)
-               runtime·printf("pcvalue start f=%s [%p] pc=%p targetpc=%p value=%d tab=%p\n",
-                       runtime·funcname(f), f, pc, targetpc, value, p);
-       
-       while(step(&p, &pc, &value, pc == f->entry)) {
-               if(debug)
-                       runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
-               if(targetpc < pc)
-                       return value;
-       }
-       
-       // If there was a table, it should have covered all program counters.
-       // If not, something is wrong.
-       if(runtime·panicking || !strict)
-               return -1;
-       runtime·printf("runtime: invalid pc-encoded table f=%s pc=%p targetpc=%p tab=%p\n",
-               runtime·funcname(f), pc, targetpc, p);
-       p = (byte*)f + off;
-       pc = f->entry;
-       value = -1;
-       
-       while(step(&p, &pc, &value, pc == f->entry))
-               runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
-       
-       runtime·throw("invalid runtime symbol table");
-       return -1;
-}
-
-static String unknown = { (uint8*)"?", 1 };
-
-int8*
-runtime·funcname(Func *f)
-{
-       if(f == nil || f->nameoff == 0)
-               return nil;
-       return (int8*)(runtime·pclntab + f->nameoff);
-}
-
-static int32
-funcline(Func *f, uintptr targetpc, String *file, bool strict)
-{
-       int32 line;
-       int32 fileno;
-
-       *file = unknown;
-       fileno = pcvalue(f, f->pcfile, targetpc, strict);
-       line = pcvalue(f, f->pcln, targetpc, strict);
-       if(fileno == -1 || line == -1 || fileno >= runtime·nfiletab) {
-               // runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line);
-               return 0;
-       }
-       *file = runtime·gostringnocopy(runtime·pclntab + filetab[fileno]);
-       return line;
-}
-
-int32
-runtime·funcline(Func *f, uintptr targetpc, String *file)
-{
-       return funcline(f, targetpc, file, true);
-}
-
-int32
-runtime·funcspdelta(Func *f, uintptr targetpc)
-{
-       int32 x;
-       
-       x = pcvalue(f, f->pcsp, targetpc, true);
-       if(x&(sizeof(void*)-1))
-               runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
-       return x;
-}
-
-int32
-runtime·pcdatavalue(Func *f, int32 table, uintptr targetpc)
-{
-       if(table < 0 || table >= f->npcdata)
-               return -1;
-       return pcvalue(f, (&f->nfuncdata)[1+table], targetpc, true);
-}
-
-int32
-runtime·funcarglen(Func *f, uintptr targetpc)
-{
-       if(targetpc == f->entry)
-               return 0;
-       return runtime·pcdatavalue(f, PCDATA_ArgSize, targetpc-PCQuantum);
-}
-
-Func*
-runtime·findfunc(uintptr addr)
-{
-       Ftab *f;
-       int32 nf, n;
-
-       if(runtime·nftab == 0)
-               return nil;
-       if(addr < ftab[0].entry || addr >= ftab[runtime·nftab].entry)
-               return nil;
-
-       // binary search to find func with entry <= addr.
-       f = ftab;
-       nf = runtime·nftab;
-       while(nf > 0) {
-               n = nf/2;
-               if(f[n].entry <= addr && addr < f[n+1].entry)
-                       return (Func*)(runtime·pclntab + f[n].funcoff);
-               else if(addr < f[n].entry)
-                       nf = n;
-               else {
-                       f += n+1;
-                       nf -= n+1;
-               }
-       }
-
-       // can't get here -- we already checked above
-       // that the address was in the table bounds.
-       // this can only happen if the table isn't sorted
-       // by address or if the binary search above is buggy.
-       runtime·prints("findfunc unreachable\n");
-       return nil;
-}
-
-static bool
-hasprefix(String s, int8 *p)
-{
-       int32 i;
-
-       for(i=0; i<s.len; i++) {
-               if(p[i] == 0)
-                       return 1;
-               if(p[i] != s.str[i])
-                       return 0;
-       }
-       return p[i] == 0;
-}
-
-static bool
-contains(String s, int8 *p)
-{
-       int32 i;
-
-       if(p[0] == 0)
-               return 1;
-       for(i=0; i<s.len; i++) {
-               if(s.str[i] != p[0])
-                       continue;
-               if(hasprefix((String){s.str + i, s.len - i}, p))
-                       return 1;
-       }
-       return 0;
-}
-
-bool
-runtime·showframe(Func *f, G *gp)
-{
-       static int32 traceback = -1;
-       String name;
-
-       if(g->m->throwing > 0 && gp != nil && (gp == g->m->curg || gp == g->m->caughtsig))
-               return 1;
-       if(traceback < 0)
-               traceback = runtime·gotraceback(nil);
-       name = runtime·gostringnocopy((uint8*)runtime·funcname(f));
-
-       // Special case: always show runtime.panic frame, so that we can
-       // see where a panic started in the middle of a stack trace.
-       // See golang.org/issue/5832.
-       if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
-               return 1;
-
-       return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
-}
index 880a78d4811e179e5e71ef174d26e84d58c1d31f..bd9e9924c400cda25542057c9c20009fe0790603 100644 (file)
@@ -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
-}
index 6286c9dac151a1c5e6ecf7779f5f03bf3dacc00f..adb03440b39627eef2c35176356202c47a46c9cf 100644 (file)
@@ -30,10 +30,22 @@ import "unsafe"
 
 const usesLR = GOARCH != "amd64" && GOARCH != "amd64p32" && GOARCH != "386"
 
-// jmpdeferPC is the PC at the beginning of the jmpdefer assembly function.
-// The traceback needs to recognize it on link register architectures.
-var jmpdeferPC = funcPC(jmpdefer)
-var deferprocPC = funcPC(deferproc)
+var (
+       deferprocPC = funcPC(deferproc)
+       goexitPC    = funcPC(goexit)
+       jmpdeferPC  = funcPC(jmpdefer)
+       lessstackPC = funcPC(lessstack)
+       mcallPC     = funcPC(mcall)
+       morestackPC = funcPC(morestack)
+       mstartPC    = funcPC(mstart)
+       newprocPC   = funcPC(newproc)
+       newstackPC  = funcPC(newstack)
+       onMPC       = funcPC(onM)
+       rt0_goPC    = funcPC(rt0_go)
+       sigpanicPC  = funcPC(sigpanic)
+
+       externalthreadhandlerp uintptr // initialized elsewhere
+)
 
 // System-specific hook. See traceback_windows.go
 var systraceback func(*_func, *stkframe, *g, bool, func(*stkframe, unsafe.Pointer) bool, unsafe.Pointer) (changed, aborted bool)
@@ -112,7 +124,7 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
                //      fp is the frame pointer (caller's stack pointer) at that program counter, or nil if unknown.
                //      stk is the stack containing sp.
                //      The caller's program counter is lr, unless lr is zero, in which case it is *(uintptr*)sp.
-               if frame.pc == uintptr(unsafe.Pointer(&lessstack)) {
+               if frame.pc == lessstackPC {
                        // Hit top of stack segment.  Unwind to next segment.
                        frame.pc = stk.gobuf.pc
                        frame.sp = stk.gobuf.sp
@@ -213,7 +225,7 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
                                frame.arglen = uintptr(f.args)
                        } else if flr == nil {
                                frame.arglen = 0
-                       } else if frame.lr == uintptr(unsafe.Pointer(&lessstack)) {
+                       } else if frame.lr == lessstackPC {
                                frame.arglen = uintptr(stk.argsize)
                        } else {
                                i := funcarglen(flr, frame.lr)
@@ -342,8 +354,8 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
                n++
 
        skipped:
-               waspanic = f.entry == uintptr(unsafe.Pointer(&sigpanic))
-               wasnewproc = f.entry == uintptr(unsafe.Pointer(&newproc)) || f.entry == deferprocPC
+               waspanic = f.entry == sigpanicPC
+               wasnewproc = f.entry == newprocPC || f.entry == deferprocPC
 
                // Do not unwind past the bottom of the stack.
                if flr == nil {
@@ -448,8 +460,6 @@ func gentraceback(pc0 uintptr, sp0 uintptr, lr0 uintptr, gp *g, skip int, pcbuf
        return n
 }
 
-func showframe(*_func, *g) bool
-
 func printcreatedby(gp *g) {
        // Show what created goroutine, except main goroutine (goid 1).
        pc := gp.gopc
@@ -499,6 +509,40 @@ func gcallers(gp *g, skip int, pcbuf *uintptr, m int) int {
        return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, pcbuf, m, nil, nil, false)
 }
 
+func showframe(f *_func, gp *g) bool {
+       g := getg()
+       if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig) {
+               return true
+       }
+       traceback := gotraceback(nil)
+       name := gostringnocopy(funcname(f))
+
+       // Special case: always show runtime.panic frame, so that we can
+       // see where a panic started in the middle of a stack trace.
+       // See golang.org/issue/5832.
+       if name == "runtime.panic" {
+               return true
+       }
+
+       return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.")
+}
+
+func contains(s, t string) bool {
+       if len(t) == 0 {
+               return true
+       }
+       for i := 0; i < len(s); i++ {
+               if s[i] == t[0] && hasprefix(s[i:], t) {
+                       return true
+               }
+       }
+       return false
+}
+
+func hasprefix(s, t string) bool {
+       return len(s) >= len(t) && s[:len(t)] == t
+}
+
 var gStatusStrings = [...]string{
        _Gidle:      "idle",
        _Grunnable:  "runnable",
@@ -583,22 +627,6 @@ func tracebackothers(me *g) {
        unlock(&allglock)
 }
 
-func mstart()
-func morestack()
-func rt0_go()
-
-var (
-       goexitPC    = funcPC(goexit)
-       mstartPC    = funcPC(mstart)
-       mcallPC     = funcPC(mcall)
-       onMPC       = funcPC(onM)
-       morestackPC = funcPC(morestack)
-       lessstackPC = funcPC(lessstack)
-       rt0_goPC    = funcPC(rt0_go)
-
-       externalthreadhandlerp uintptr // initialized elsewhere
-)
-
 // Does f mark the top of a goroutine stack?
 func topofstack(f *_func) bool {
        pc := f.entry