aboutsummaryrefslogtreecommitdiff
path: root/src/pkg
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2013-07-16 09:41:38 -0400
committerRuss Cox <rsc@golang.org>2013-07-16 09:41:38 -0400
commit5d363c6357ebcacc8ba7c24420f7cfd2e530591a (patch)
treebc168c1cbd6483edd35aa74cdab1481812cdfc3c /src/pkg
parent63e0ddc7bf0c7523d826331ff51a551c5040b50b (diff)
downloadgo-5d363c6357ebcacc8ba7c24420f7cfd2e530591a.tar.xz
cmd/ld, runtime: new in-memory symbol table format
Design at http://golang.org/s/go12symtab. This enables some cleanup of the garbage collector metadata that will be done in future CLs. This CL does not move the old symtab and pclntab back into an unmapped section of the file. That's a bit tricky and will be done separately. Fixes #4020. R=golang-dev, dave, cshapiro, iant, r CC=golang-dev, nigeltao https://golang.org/cl/11085043
Diffstat (limited to 'src/pkg')
-rw-r--r--src/pkg/runtime/asm_386.s8
-rw-r--r--src/pkg/runtime/asm_amd64.s8
-rw-r--r--src/pkg/runtime/asm_arm.s8
-rw-r--r--src/pkg/runtime/extern.go24
-rw-r--r--src/pkg/runtime/mgc0.c9
-rw-r--r--src/pkg/runtime/runtime.c3
-rw-r--r--src/pkg/runtime/runtime.h30
-rw-r--r--src/pkg/runtime/symtab.c659
-rw-r--r--src/pkg/runtime/traceback_arm.c41
-rw-r--r--src/pkg/runtime/traceback_x86.c54
10 files changed, 238 insertions, 606 deletions
diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s
index f2265cbf01..3bf3321156 100644
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -573,9 +573,9 @@ havem:
MOVL BP, 0(DI)
// Push arguments to cgocallbackg.
- // Frame size here must match the frame size above
+ // Frame size here must match the frame size above plus the pushes
// to trick traceback routines into doing the right thing.
- SUBL $12, DI
+ SUBL $20, DI
MOVL AX, 0(DI)
MOVL BX, 4(DI)
MOVL DX, 8(DI)
@@ -587,9 +587,9 @@ havem:
// Restore g->sched (== m->curg->sched) from saved values.
get_tls(CX)
MOVL g(CX), SI
- MOVL 12(SP), BP
+ MOVL 20(SP), BP
MOVL BP, (g_sched+gobuf_pc)(SI)
- LEAL (12+4)(SP), DI
+ LEAL (20+4)(SP), DI
MOVL DI, (g_sched+gobuf_sp)(SI)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s
index 363e680db9..d7afe4b1b1 100644
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -609,9 +609,9 @@ havem:
MOVQ BP, 0(DI)
// Push arguments to cgocallbackg.
- // Frame size here must match the frame size above
+ // Frame size here must match the frame size above plus the pushes
// to trick traceback routines into doing the right thing.
- SUBQ $24, DI
+ SUBQ $40, DI
MOVQ AX, 0(DI)
MOVQ BX, 8(DI)
MOVQ DX, 16(DI)
@@ -623,9 +623,9 @@ havem:
// Restore g->sched (== m->curg->sched) from saved values.
get_tls(CX)
MOVQ g(CX), SI
- MOVQ 24(SP), BP
+ MOVQ 40(SP), BP
MOVQ BP, (g_sched+gobuf_pc)(SI)
- LEAQ (24+8)(SP), DI
+ LEAQ (40+8)(SP), DI
MOVQ DI, (g_sched+gobuf_sp)(SI)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index 31bbca6afa..8a3597e892 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -368,12 +368,12 @@ havem:
MOVW (g_sched+gobuf_sp)(g), R4 // prepare stack as R4
// Push gobuf.pc
+ // Frame size here must match the frame size above plus the push
+ // to trick traceback routines into doing the right thing.
MOVW (g_sched+gobuf_pc)(g), R5
- MOVW.W R5, -16(R4)
+ MOVW.W R5, -20(R4)
// Push arguments to cgocallbackg.
- // Frame size here must match the frame size above
- // to trick traceback routines into doing the right thing.
MOVW R0, 4(R4)
MOVW R1, 8(R4)
MOVW R2, 12(R4)
@@ -385,7 +385,7 @@ havem:
// Restore g->sched (== m->curg->sched) from saved values.
MOVW 0(R13), R5
MOVW R5, (g_sched+gobuf_pc)(g)
- ADD $(12+4), R13, R4
+ ADD $(16+4), R13, R4
MOVW R4, (g_sched+gobuf_sp)(g)
// Switch back to m->g0's stack and restore m->g0->sched.sp.
diff --git a/src/pkg/runtime/extern.go b/src/pkg/runtime/extern.go
index 950c0be257..cc25de1554 100644
--- a/src/pkg/runtime/extern.go
+++ b/src/pkg/runtime/extern.go
@@ -78,18 +78,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
// It returns the number of entries written to pc.
func Callers(skip int, pc []uintptr) int
-type Func struct { // Keep in sync with runtime.h:struct Func
- name string
- typ string // go type string
- src string // src file name
- pcln []byte // pc/ln tab for this func
- entry uintptr // entry pc
- pc0 uintptr // starting pc, ln for table
- ln0 int32
- frame int32 // stack frame size
- args int32 // in/out args size
- locals int32 // locals size
- ptrs []int32 // pointer map
+type Func struct {
+ opaque struct{} // unexported field to disallow conversions
}
// FuncForPC returns a *Func describing the function that contains the
@@ -97,10 +87,14 @@ type Func struct { // Keep in sync with runtime.h:struct Func
func FuncForPC(pc uintptr) *Func
// Name returns the name of the function.
-func (f *Func) Name() string { return f.name }
+func (f *Func) Name() string {
+ return funcname_go(f)
+}
// Entry returns the entry address of the function.
-func (f *Func) Entry() uintptr { return f.entry }
+func (f *Func) Entry() uintptr {
+ return funcentry_go(f)
+}
// FileLine returns the file name and line number of the
// source code corresponding to the program counter pc.
@@ -112,6 +106,8 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) {
// implemented in symtab.c
func funcline_go(*Func, uintptr) (string, int)
+func funcname_go(*Func) string
+func funcentry_go(*Func) uintptr
// SetFinalizer sets the finalizer associated with x to f.
// When the garbage collector finds an unreachable block
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c
index a819135901..1349aa7726 100644
--- a/src/pkg/runtime/mgc0.c
+++ b/src/pkg/runtime/mgc0.c
@@ -1390,7 +1390,7 @@ addframeroots(Stkframe *frame, void*)
Func *f;
byte *ap;
int32 i, j, nuintptr;
- uint32 w, b;
+ uint32 w, b, *ptrs;
// Scan local variables if stack frame has been allocated.
if(frame->varlen > 0)
@@ -1399,11 +1399,12 @@ addframeroots(Stkframe *frame, void*)
// Scan arguments.
// Use pointer information if known.
f = frame->fn;
- if(f->args > 0 && f->ptrs.array != nil) {
+ if(f->args > 0 && f->ptrslen > 0) {
ap = frame->argp;
nuintptr = f->args / sizeof(uintptr);
- for(i = 0; i < f->ptrs.len; i++) {
- w = ((uint32*)f->ptrs.array)[i];
+ ptrs = (uint32*)((byte*)f + f->ptrsoff);
+ for(i = 0; i < f->ptrslen; i++) {
+ w = ptrs[i];
b = 1;
j = nuintptr;
if(j > 32)
diff --git a/src/pkg/runtime/runtime.c b/src/pkg/runtime/runtime.c
index f0571f1899..9b5f6c8ca3 100644
--- a/src/pkg/runtime/runtime.c
+++ b/src/pkg/runtime/runtime.c
@@ -294,12 +294,11 @@ runtime·Caller(intgo skip, uintptr retpc, String retfile, intgo retline, bool r
retbool = true; // have retpc at least
} else {
retpc = rpc[1];
- retfile = f->src;
pc = retpc;
g = runtime·findfunc(rpc[0]);
if(pc > f->entry && (g == nil || g->entry != (uintptr)runtime·sigpanic))
pc--;
- retline = runtime·funcline(f, pc);
+ retline = runtime·funcline(f, pc, &retfile);
retbool = true;
}
FLUSH(&retpc);
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 49503ab41b..3940c30447 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -401,21 +401,25 @@ enum
SigIgnored = 1<<6, // the signal was ignored before we registered for it
};
-// NOTE(rsc): keep in sync with extern.go:/type.Func.
-// Eventually, the loaded symbol table should be closer to this form.
+// layout of in-memory per-function information prepared by linker
+// See http://golang.org/s/go12symtab.
struct Func
{
- String name;
- String type; // go type string
- String src; // src file name
- Slice pcln; // pc/ln tab for this func
- uintptr entry; // entry pc
- uintptr pc0; // starting pc, ln for table
- int32 ln0;
- int32 frame; // stack frame size
+ String *name; // function name
+ uintptr entry; // start pc
+
+ // TODO: Remove these fields.
int32 args; // in/out args size
int32 locals; // locals size
- Slice ptrs; // pointer map
+ int32 frame; // legacy frame size; use pcsp if possible
+ int32 ptrsoff;
+ int32 ptrslen;
+
+ int32 pcsp;
+ int32 pcfile;
+ int32 pcln;
+ int32 npcdata;
+ int32 nfuncdata;
};
// layout of Itab known to compilers
@@ -790,7 +794,9 @@ void runtime·unminit(void);
void runtime·signalstack(byte*, int32);
void runtime·symtabinit(void);
Func* runtime·findfunc(uintptr);
-int32 runtime·funcline(Func*, uintptr);
+int32 runtime·funcline(Func*, uintptr, String*);
+int32 runtime·funcarglen(Func*, uintptr);
+int32 runtime·funcspdelta(Func*, uintptr);
void* runtime·stackalloc(uint32);
void runtime·stackfree(void*, uintptr);
MCache* runtime·allocmcache(void);
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c
index 7fd700e36a..a96c0ead80 100644
--- a/src/pkg/runtime/symtab.c
+++ b/src/pkg/runtime/symtab.c
@@ -3,52 +3,7 @@
// license that can be found in the LICENSE file.
// Runtime symbol table parsing.
-//
-// The Go tools use a symbol table derived from the Plan 9 symbol table
-// format. The symbol table is kept in its own section treated as
-// read-only memory when the binary is running: the binary consults the
-// table.
-//
-// The format used by Go 1.0 was basically the Plan 9 format. Each entry
-// is variable sized but had this format:
-//
-// 4-byte value, big endian
-// 1-byte type ([A-Za-z] + 0x80)
-// name, NUL terminated (or for 'z' and 'Z' entries, double-NUL terminated)
-// 4-byte Go type address, big endian (new in Go)
-//
-// In order to support greater interoperation with standard toolchains,
-// Go 1.1 uses a more flexible yet smaller encoding of the entries.
-// The overall structure is unchanged from Go 1.0 and, for that matter,
-// from Plan 9.
-//
-// The Go 1.1 table is a re-encoding of the data in a Go 1.0 table.
-// To identify a new table as new, it begins one of two eight-byte
-// sequences:
-//
-// FF FF FF FD 00 00 00 xx - big endian new table
-// FD FF FF FF 00 00 00 xx - little endian new table
-//
-// This sequence was chosen because old tables stop at an entry with type
-// 0, so old code reading a new table will see only an empty table. The
-// first four bytes are the target-endian encoding of 0xfffffffd. The
-// final xx gives AddrSize, the width of a full-width address.
-//
-// After that header, each entry is encoded as follows.
-//
-// 1-byte type (0-51 + two flag bits)
-// AddrSize-byte value, host-endian OR varint-encoded value
-// AddrSize-byte Go type address OR nothing
-// [n] name, terminated as before
-//
-// The type byte comes first, but 'A' encodes as 0 and 'a' as 26, so that
-// the type itself is only in the low 6 bits. The upper two bits specify
-// the format of the next two fields. If the 0x40 bit is set, the value
-// is encoded as an full-width 4- or 8-byte target-endian word. Otherwise
-// the value is a varint-encoded number. If the 0x80 bit is set, the Go
-// type is present, again as a 4- or 8-byte target-endian word. If not,
-// there is no Go type in this entry. The NUL-terminated name ends the
-// entry.
+// See http://golang.org/s/go12symtab for an overview.
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
@@ -56,549 +11,221 @@
#include "arch_GOARCH.h"
#include "malloc.h"
-extern byte pclntab[], epclntab[], symtab[], esymtab[];
-
-typedef struct Sym Sym;
-struct Sym
+typedef struct Ftab Ftab;
+struct Ftab
{
- uintptr value;
- byte symtype;
- byte *name;
-// byte *gotype;
+ uintptr entry;
+ Func *func;
};
-static uintptr mainoffset;
-
-extern void main·main(void);
-
-static uintptr
-readword(byte **pp, byte *ep)
-{
- byte *p;
+extern uintptr functab[];
- p = *pp;
- if(ep - p < sizeof(void*)) {
- *pp = ep;
- return 0;
- }
- *pp = p + sizeof(void*);
+static Ftab *ftab;
+static uintptr nftab;
+extern String *filetab[];
+static uintptr nfiletab;
- // Hairy, but only one of these four cases gets compiled.
- if(sizeof(void*) == 8) {
- if(BigEndian) {
- return ((uint64)p[0]<<56) | ((uint64)p[1]<<48) | ((uint64)p[2]<<40) | ((uint64)p[3]<<32) |
- ((uint64)p[4]<<24) | ((uint64)p[5]<<16) | ((uint64)p[6]<<8) | ((uint64)p[7]);
- }
- return ((uint64)p[7]<<56) | ((uint64)p[6]<<48) | ((uint64)p[5]<<40) | ((uint64)p[4]<<32) |
- ((uint64)p[3]<<24) | ((uint64)p[2]<<16) | ((uint64)p[1]<<8) | ((uint64)p[0]);
- }
- if(BigEndian) {
- return ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]);
- }
- return ((uint32)p[3]<<24) | ((uint32)p[2]<<16) | ((uint32)p[1]<<8) | ((uint32)p[0]);
-}
+static String end = { (uint8*)"end", 3 };
-// Walk over symtab, calling fn(&s) for each symbol.
-static void
-walksymtab(void (*fn)(Sym*))
+void
+runtime·symtabinit(void)
{
- byte *p, *ep, *q;
- Sym s;
- int32 widevalue, havetype, shift;
+ int32 i, j;
- p = symtab;
- ep = esymtab;
-
- // Table must begin with correct magic number.
- if(ep - p < 8 || p[4] != 0x00 || p[5] != 0x00 || p[6] != 0x00 || p[7] != sizeof(void*))
- return;
- if(BigEndian) {
- if(p[0] != 0xff || p[1] != 0xff || p[2] != 0xff || p[3] != 0xfd)
- return;
- } else {
- if(p[0] != 0xfd || p[1] != 0xff || p[2] != 0xff || p[3] != 0xff)
- return;
- }
- p += 8;
-
- while(p < ep) {
- s.symtype = p[0]&0x3F;
- widevalue = p[0]&0x40;
- havetype = p[0]&0x80;
- if(s.symtype < 26)
- s.symtype += 'A';
- else
- s.symtype += 'a' - 26;
- p++;
-
- // Value, either full-width or varint-encoded.
- if(widevalue) {
- s.value = readword(&p, ep);
- } else {
- s.value = 0;
- shift = 0;
- while(p < ep && (p[0]&0x80) != 0) {
- s.value |= (uintptr)(p[0]&0x7F)<<shift;
- shift += 7;
- p++;
- }
- if(p >= ep)
- break;
- s.value |= (uintptr)p[0]<<shift;
- p++;
- }
-
- // Go type, if present. Ignored but must skip over.
- if(havetype)
- readword(&p, ep);
-
- // Name.
- if(ep - p < 2)
- break;
-
- s.name = p;
- if(s.symtype == 'z' || s.symtype == 'Z') {
- // path reference string - skip first byte,
- // then 2-byte pairs ending at two zeros.
- q = p+1;
- for(;;) {
- if(q+2 > ep)
- return;
- if(q[0] == '\0' && q[1] == '\0')
- break;
- q += 2;
- }
- p = q+2;
- }else{
- q = runtime·mchr(p, '\0', ep);
- if(q == nil)
- break;
- p = q+1;
- }
+ ftab = (Ftab*)(functab+1);
+ nftab = functab[0];
- fn(&s);
+ 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) {
+ runtime·printf("function symbol table not sorted by program counter: %p %S > %p %S", ftab[i].entry, *ftab[i].func->name, ftab[i+1].entry, i+1 == nftab ? end : *ftab[i+1].func->name);
+ for(j=0; j<=i; j++)
+ runtime·printf("\t%p %S\n", ftab[j].entry, *ftab[j].func->name);
+ runtime·throw("invalid runtime symbol table");
+ }
}
+ nfiletab = (uintptr)filetab[0];
}
-// Symtab walker; accumulates info about functions.
-
-static Func *func;
-static int32 nfunc;
-
-static byte **fname;
-static int32 nfname;
-
-static uintptr lastvalue;
-
-static void
-dofunc(Sym *sym)
+static uint32
+readvarint(byte **pp)
{
- Func *f;
- uintgo cap;
+ byte *p;
+ uint32 v;
+ int32 shift;
- switch(sym->symtype) {
- case 't':
- case 'T':
- case 'l':
- case 'L':
- if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
- break;
- if(sym->value < lastvalue) {
- runtime·printf("runtime: symbols out of order: %p before %p\n", lastvalue, sym->value);
- runtime·throw("malformed symbol table");
- }
- lastvalue = sym->value;
- if(func == nil) {
- nfunc++;
- break;
- }
- f = &func[nfunc++];
- f->name = runtime·gostringnocopy(sym->name);
- f->entry = sym->value;
- if(sym->symtype == 'L' || sym->symtype == 'l')
- f->frame = -sizeof(uintptr);
- break;
- case 'm':
- if(nfunc <= 0 || func == nil)
- break;
- if(runtime·strcmp(sym->name, (byte*)".frame") == 0)
- func[nfunc-1].frame = sym->value;
- else if(runtime·strcmp(sym->name, (byte*)".locals") == 0)
- func[nfunc-1].locals = sym->value;
- else if(runtime·strcmp(sym->name, (byte*)".args") == 0)
- func[nfunc-1].args = sym->value;
- else if(runtime·strcmp(sym->name, (byte*)".nptrs") == 0) {
- if(sym->value != func[nfunc-1].args/sizeof(uintptr)) {
- runtime·printf("runtime: pointer map size and argument size disagree\n");
- runtime·throw("mangled symbol table");
- }
- cap = ROUND(sym->value, 32) / 32;
- func[nfunc-1].ptrs.array = runtime·persistentalloc(cap*sizeof(uint32), sizeof(uint32));
- func[nfunc-1].ptrs.len = 0;
- func[nfunc-1].ptrs.cap = cap;
- } else if(runtime·strcmp(sym->name, (byte*)".ptrs") == 0) {
- if(func[nfunc-1].ptrs.len >= func[nfunc-1].ptrs.cap) {
- runtime·printf("runtime: more pointer map entries read than argument words\n");
- runtime·throw("mangled symbol table");
- }
- ((uint32*)func[nfunc-1].ptrs.array)[func[nfunc-1].ptrs.len++] = sym->value;
- } else {
- runtime·printf("runtime: invalid '%c' symbol named '%s'\n", (int8)sym->symtype, sym->name);
- runtime·throw("mangled symbol table");
- }
- break;
- case 'f':
- if(fname == nil) {
- if(sym->value >= nfname) {
- if(sym->value >= 0x10000) {
- runtime·printf("runtime: invalid symbol file index %p\n", sym->value);
- runtime·throw("mangled symbol table");
- }
- nfname = sym->value+1;
- }
+ v = 0;
+ p = *pp;
+ for(shift = 0;; shift += 7) {
+ v |= (*p & 0x7F) << shift;
+ if(!(*p++ & 0x80))
break;
- }
- fname[sym->value] = sym->name;
- break;
}
+ *pp = p;
+ return v;
}
-// put together the path name for a z entry.
-// the f entries have been accumulated into fname already.
-// returns the length of the path name.
-static int32
-makepath(byte *buf, int32 nbuf, byte *path)
+static uintptr
+funcdata(Func *f, int32 i)
{
- int32 n, len;
- byte *p, *ep, *q;
+ byte *p;
- if(nbuf <= 0)
+ if(i < 0 || i >= f->nfuncdata)
return 0;
-
- p = buf;
- ep = buf + nbuf;
- *p = '\0';
- for(;;) {
- if(path[0] == 0 && path[1] == 0)
- break;
- n = (path[0]<<8) | path[1];
- path += 2;
- if(n >= nfname)
- break;
- q = fname[n];
- len = runtime·findnull(q);
- if(p+1+len >= ep)
- break;
- if(p > buf && p[-1] != '/')
- *p++ = '/';
- runtime·memmove(p, q, len+1);
- p += len;
- }
- return p - buf;
-}
-
-static String
-gostringn(byte *p, int32 l)
-{
- String s;
-
- if(l == 0)
- return runtime·emptystring;
- s.len = l;
- s.str = runtime·persistentalloc(l, 1);
- runtime·memmove(s.str, p, l);
- return s;
-}
-
-static struct
-{
- String srcstring;
- int32 aline;
- int32 delta;
-} *files;
-
-enum { maxfiles = 200 };
-
-// walk symtab accumulating path names for use by pc/ln table.
-// don't need the full generality of the z entry history stack because
-// there are no includes in go (and only sensible includes in our c);
-// assume code only appear in top-level files.
-static void
-dosrcline(Sym *sym)
-{
- #pragma dataflag 16 // no pointers
- static byte srcbuf[1000];
- static int32 incstart;
- static int32 nfunc, nfile, nhist;
- Func *f;
- int32 i, l;
-
- switch(sym->symtype) {
- case 't':
- case 'T':
- if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
- break;
- f = &func[nfunc++];
- // find source file
- for(i = 0; i < nfile - 1; i++) {
- if (files[i+1].aline > f->ln0)
- break;
- }
- f->src = files[i].srcstring;
- f->ln0 -= files[i].delta;
- break;
- case 'z':
- if(sym->value == 1) {
- // entry for main source file for a new object.
- l = makepath(srcbuf, sizeof srcbuf, sym->name+1);
- nhist = 0;
- nfile = 0;
- if(nfile == maxfiles)
- return;
- files[nfile].srcstring = gostringn(srcbuf, l);
- files[nfile].aline = 0;
- files[nfile++].delta = 0;
- } else {
- // push or pop of included file.
- l = makepath(srcbuf, sizeof srcbuf, sym->name+1);
- if(srcbuf[0] != '\0') {
- if(nhist++ == 0)
- incstart = sym->value;
- if(nhist == 0 && nfile < maxfiles) {
- // new top-level file
- files[nfile].srcstring = gostringn(srcbuf, l);
- files[nfile].aline = sym->value;
- // this is "line 0"
- files[nfile++].delta = sym->value - 1;
- }
- }else{
- if(--nhist == 0)
- files[nfile-1].delta += sym->value - incstart;
- }
- }
- }
+ p = (byte*)&f->nfuncdata + 4 + f->npcdata*4;
+ if(sizeof(void*) == 8 && ((uintptr)p & 4))
+ p += 4;
+ return ((uintptr*)p)[i];
}
-// Interpret pc/ln table, saving the subpiece for each func.
-static void
-splitpcln(void)
+// Return associated data value for targetpc in func f.
+// (Source file is f->src.)
+static int32
+pcvalue(Func *f, int32 off, uintptr targetpc)
{
- int32 line;
+ byte *p;
uintptr pc;
- byte *p, *ep;
- Func *f, *ef;
- int32 pcquant;
+ int32 value, vdelta, pcshift;
+ uint32 uvdelta, pcdelta;
- if(pclntab == epclntab || nfunc == 0)
- return;
+ enum {
+ debug = 0
+ };
switch(thechar) {
case '5':
- pcquant = 4;
+ pcshift = 2;
break;
default: // 6, 8
- pcquant = 1;
+ pcshift = 0;
break;
}
- // pc/ln table bounds
- p = pclntab;
- ep = epclntab;
+ // 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 = (byte*)f + off;
+ pc = f->entry;
+ value = -1;
- f = func;
- ef = func + nfunc;
- pc = func[0].entry; // text base
- f->pcln.array = p;
- f->pc0 = pc;
- line = 0;
+ if(debug && !runtime·panicking)
+ runtime·printf("pcvalue start f=%S [%p] pc=%p targetpc=%p value=%d tab=%p\n",
+ *f->name, f, pc, targetpc, value, p);
+
for(;;) {
- while(p < ep && *p > 128)
- pc += pcquant * (*p++ - 128);
- // runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line);
- if(*p == 0) {
- if(p+5 > ep)
- break;
- // 4 byte add to line
- line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
- p += 5;
- } else if(*p <= 64)
- line += *p++;
+ uvdelta = readvarint(&p);
+ if(uvdelta == 0 && pc != f->entry)
+ break;
+ if(uvdelta&1)
+ uvdelta = ~(uvdelta>>1);
else
- line -= *p++ - 64;
-
- // pc, line now match.
- // Because the state machine begins at pc==entry and line==0,
- // it can happen - just at the beginning! - that the update may
- // have updated line but left pc alone, to tell us the true line
- // number for pc==entry. In that case, update f->ln0.
- // Having the correct initial line number is important for choosing
- // the correct file in dosrcline above.
- if(f == func && pc == f->pc0) {
- f->pcln.array = p;
- f->pc0 = pc + pcquant;
- f->ln0 = line;
- }
-
- if(f < ef && pc >= (f+1)->entry) {
- f->pcln.len = p - f->pcln.array;
- f->pcln.cap = f->pcln.len;
- do
- f++;
- while(f < ef && pc >= (f+1)->entry);
- f->pcln.array = p;
- // pc0 and ln0 are the starting values for
- // the loop over f->pcln, so pc must be
- // adjusted by the same pcquant update
- // that we're going to do as we continue our loop.
- f->pc0 = pc + pcquant;
- f->ln0 = line;
- }
-
- pc += pcquant;
- }
- if(f < ef) {
- f->pcln.len = p - f->pcln.array;
- f->pcln.cap = f->pcln.len;
+ uvdelta >>= 1;
+ vdelta = (int32)uvdelta;
+ pcdelta = readvarint(&p) << pcshift;
+ value += vdelta;
+ pc += pcdelta;
+ 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.
+ runtime·printf("runtime: invalid pc-encoded table f=%S pc=%p targetpc=%p tab=%p\n",
+ *f->name, pc, targetpc, p);
+ runtime·throw("invalid runtime symbol table");
+ return -1;
}
+static String unknown = { (uint8*)"?", 1 };
-// Return actual file line number for targetpc in func f.
-// (Source file is f->src.)
-// NOTE(rsc): If you edit this function, also edit extern.go:/FileLine
int32
-runtime·funcline(Func *f, uintptr targetpc)
+runtime·funcline(Func *f, uintptr targetpc, String *file)
{
- byte *p, *ep;
- uintptr pc;
int32 line;
- int32 pcquant;
+ int32 fileno;
- enum {
- debug = 0
- };
-
- switch(thechar) {
- case '5':
- pcquant = 4;
- break;
- default: // 6, 8
- pcquant = 1;
- break;
+ *file = unknown;
+ fileno = pcvalue(f, f->pcfile, targetpc);
+ line = pcvalue(f, f->pcln, targetpc);
+ if(fileno == -1 || line == -1 || fileno >= nfiletab) {
+ // runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line);
+ return 0;
}
+ *file = *filetab[fileno];
+ return line;
+}
- p = f->pcln.array;
- ep = p + f->pcln.len;
- pc = f->pc0;
- line = f->ln0;
- if(debug && !runtime·panicking)
- runtime·printf("funcline start pc=%p targetpc=%p line=%d tab=%p+%d\n",
- pc, targetpc, line, p, (int32)f->pcln.len);
- for(;;) {
- // Table is a sequence of updates.
-
- // Each update says first how to adjust the pc,
- // in possibly multiple instructions...
- while(p < ep && *p > 128)
- pc += pcquant * (*p++ - 128);
-
- if(debug && !runtime·panicking)
- runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line);
-
- // If the pc has advanced too far or we're out of data,
- // stop and the last known line number.
- if(pc > targetpc || p >= ep)
- break;
+int32
+runtime·funcspdelta(Func *f, uintptr targetpc)
+{
+ int32 x;
+
+ x = pcvalue(f, f->pcsp, targetpc);
+ if(x&(sizeof(void*)-1))
+ runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
+ return x;
+}
- // ... and then how to adjust the line number,
- // in a single instruction.
- if(*p == 0) {
- if(p+5 > ep)
- break;
- line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
- p += 5;
- } else if(*p <= 64)
- line += *p++;
- else
- line -= *p++ - 64;
- // Now pc, line pair is consistent.
- if(debug && !runtime·panicking)
- runtime·printf("pc=%p targetpc=%p line=%d\n", pc, targetpc, line);
+static int32
+pcdatavalue(Func *f, int32 table, uintptr targetpc)
+{
+ if(table < 0 || table >= f->npcdata)
+ return -1;
+ return pcvalue(f, (&f->nfuncdata)[1+table], targetpc);
+}
- // PC increments implicitly on each iteration.
- pc += pcquant;
- }
- return line;
+int32
+runtime·funcarglen(Func *f, uintptr targetpc)
+{
+ return pcdatavalue(f, 0, targetpc);
}
void
runtime·funcline_go(Func *f, uintptr targetpc, String retfile, intgo retline)
{
- retfile = f->src;
- retline = runtime·funcline(f, targetpc);
- FLUSH(&retfile);
+ retline = runtime·funcline(f, targetpc, &retfile);
FLUSH(&retline);
}
void
-runtime·symtabinit(void)
+runtime·funcname_go(Func *f, String ret)
{
- extern byte etext[];
-
- if(func != nil)
- return;
-
- // Memory profiling uses this code;
- // can deadlock if the profiler ends
- // up back here.
- m->nomemprof++;
-
- // count funcs, fnames
- nfunc = 0;
- nfname = 0;
- lastvalue = 0;
- walksymtab(dofunc);
-
- // Initialize tables.
- // Memory obtained from runtime·persistentalloc() is not scanned by GC,
- // this is fine because all pointers either point into sections of the executable
- // or also obtained from persistentalloc().
- func = runtime·persistentalloc((nfunc+1)*sizeof func[0], 0);
- func[nfunc].entry = (uint64)etext;
- fname = runtime·persistentalloc(nfname*sizeof fname[0], 0);
- nfunc = 0;
- lastvalue = 0;
- walksymtab(dofunc);
-
- // split pc/ln table by func
- splitpcln();
-
- // record src file and line info for each func
- files = runtime·malloc(maxfiles * sizeof(files[0]));
- walksymtab(dosrcline);
- files = nil;
+ ret = *f->name;
+ FLUSH(&ret);
+}
- m->nomemprof--;
+void
+runtime·funcentry_go(Func *f, uintptr ret)
+{
+ ret = f->entry;
+ FLUSH(&ret);
}
Func*
runtime·findfunc(uintptr addr)
{
- Func *f;
+ Ftab *f;
int32 nf, n;
- if(nfunc == 0)
+ if(nftab == 0)
return nil;
- if(addr < func[0].entry || addr >= func[nfunc].entry)
+ if(addr < ftab[0].entry || addr >= ftab[nftab].entry)
return nil;
// binary search to find func with entry <= addr.
- f = func;
- nf = nfunc;
+ f = ftab;
+ nf = nftab;
while(nf > 0) {
n = nf/2;
if(f[n].entry <= addr && addr < f[n+1].entry)
- return &f[n];
+ return f[n].func;
else if(addr < f[n].entry)
nf = n;
else {
@@ -654,5 +281,5 @@ runtime·showframe(Func *f, G *gp)
return 1;
if(traceback < 0)
traceback = runtime·gotraceback(nil);
- return traceback > 1 || f != nil && contains(f->name, ".") && !hasprefix(f->name, "runtime.");
+ return traceback > 1 || f != nil && contains(*f->name, ".") && !hasprefix(*f->name, "runtime.");
}
diff --git a/src/pkg/runtime/traceback_arm.c b/src/pkg/runtime/traceback_arm.c
index 599f6093eb..e5a475f80f 100644
--- a/src/pkg/runtime/traceback_arm.c
+++ b/src/pkg/runtime/traceback_arm.c
@@ -15,15 +15,18 @@ void _mod(void);
void _divu(void);
void _modu(void);
+static String unknown = { (uint8*)"?", 1 };
+
int32
runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v, bool printall)
{
- int32 i, n, nprint, skip0;
+ int32 i, n, nprint, skip0, line;
uintptr x, tracepc;
bool waspanic, printing;
Func *f, *f2;
Stkframe frame;
Stktop *stk;
+ String file;
skip0 = skip;
@@ -76,11 +79,8 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
// Derive frame pointer and link register.
if(frame.lr == 0)
frame.lr = *(uintptr*)frame.sp;
- if(frame.fp == 0) {
- frame.fp = frame.sp;
- if(frame.pc > f->entry && f->frame >= sizeof(uintptr))
- frame.fp += f->frame;
- }
+ if(frame.fp == 0)
+ frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
// Derive size of arguments.
frame.argp = (byte*)frame.fp + sizeof(uintptr);
@@ -96,7 +96,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr))
frame.arglen = f2->frame; // conservative overestimate
else {
- runtime·printf("runtime: unknown argument frame size for %S\n", f->name);
+ runtime·printf("runtime: unknown argument frame size for %S\n", *f->name);
if(!printing)
runtime·throw("invalid stack");
}
@@ -113,7 +113,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.varlen = frame.fp - frame.sp;
} else {
if(f->locals > frame.fp - frame.sp) {
- runtime·printf("runtime: inconsistent locals=%p frame=%p fp=%p sp=%p for %S\n", (uintptr)f->locals, (uintptr)f->frame, frame.fp, frame.sp, f->name);
+ runtime·printf("runtime: inconsistent locals=%p frame=%p fp=%p sp=%p for %S\n", (uintptr)f->locals, (uintptr)f->frame, frame.fp, frame.sp, *f->name);
runtime·throw("invalid stack");
}
frame.varp = (byte*)frame.fp - f->locals;
@@ -138,7 +138,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
tracepc = frame.pc; // back up to CALL instruction for funcline.
if(n > 0 && frame.pc > f->entry && !waspanic)
tracepc -= sizeof(uintptr);
- runtime·printf("%S(", f->name);
+ runtime·printf("%S(", *f->name);
for(i = 0; i < frame.arglen/sizeof(uintptr); i++) {
if(i >= 5) {
runtime·prints(", ...");
@@ -149,7 +149,8 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
runtime·printhex(((uintptr*)frame.argp)[i]);
}
runtime·prints(")\n");
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
if(frame.pc > f->entry)
runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
if(m->throwing && gp == m->curg)
@@ -164,7 +165,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
waspanic = f->entry == (uintptr)runtime·sigpanic;
// Do not unwind past the bottom of the stack.
- if(f->entry == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)_rt0_go)
+ if(f->entry == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)runtime·mcall || f->entry == (uintptr)_rt0_go)
break;
// Unwind to next frame.
@@ -172,16 +173,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.lr = 0;
frame.sp = frame.fp;
frame.fp = 0;
-
- // If this was div or divu or mod or modu, the caller had
- // an extra 8 bytes on its stack. Adjust sp.
- if(f->entry == (uintptr)_div || f->entry == (uintptr)_divu || f->entry == (uintptr)_mod || f->entry == (uintptr)_modu)
- frame.sp += 8;
-
- // If this was deferproc or newproc, the caller had an extra 12.
- if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
- frame.sp += 12;
-
+
// sighandler saves the lr on stack before faking a call to sigpanic
if(waspanic) {
x = *(uintptr*)frame.sp;
@@ -203,16 +195,19 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
static void
printcreatedby(G *gp)
{
+ int32 line;
uintptr pc, tracepc;
Func *f;
+ String file;
if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil
&& runtime·showframe(f, gp) && gp->goid != 1) {
- runtime·printf("created by %S\n", f->name);
+ runtime·printf("created by %S\n", *f->name);
tracepc = pc; // back up to CALL instruction for funcline.
if(pc > f->entry)
tracepc -= sizeof(uintptr);
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
if(pc > f->entry)
runtime·printf(" +%p", (uintptr)(pc - f->entry));
runtime·printf("\n");
diff --git a/src/pkg/runtime/traceback_x86.c b/src/pkg/runtime/traceback_x86.c
index 3a9b864e89..49e1c0467f 100644
--- a/src/pkg/runtime/traceback_x86.c
+++ b/src/pkg/runtime/traceback_x86.c
@@ -16,6 +16,8 @@ void runtime·sigpanic(void);
// This code is also used for the 386 tracebacks.
// Use uintptr for an appropriate word-sized integer.
+static String unknown = { (uint8*)"?", 1 };
+
// Generic traceback. Handles runtime stack prints (pcbuf == nil),
// the runtime.Callers function (pcbuf != nil), as well as the garbage
// collector (callback != nil). A little clunky to merge these, but avoids
@@ -23,12 +25,13 @@ void runtime·sigpanic(void);
int32
runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip, uintptr *pcbuf, int32 max, void (*callback)(Stkframe*, void*), void *v, bool printall)
{
- int32 i, n, nprint;
+ int32 i, n, nprint, line;
uintptr tracepc;
bool waspanic, printing;
- Func *f, *f2;
+ Func *f, *flr;
Stkframe frame;
Stktop *stk;
+ String file;
USED(lr0);
@@ -62,33 +65,36 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.sp = stk->gobuf.sp;
frame.lr = 0;
frame.fp = 0;
+ frame.fn = nil;
if(printing && runtime·showframe(nil, gp))
runtime·printf("----- stack segment boundary -----\n");
stk = (Stktop*)stk->stackbase;
continue;
}
- if(frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil) {
- if(callback != nil)
+ f = frame.fn;
+ if(f == nil && (frame.pc <= 0x1000 || (frame.fn = f = runtime·findfunc(frame.pc)) == nil)) {
+ if(callback != nil) {
+ runtime·printf("unknown pc %p\n", frame.pc);
runtime·throw("unknown pc");
+ }
break;
}
// Found an actual function.
// Derive frame pointer and link register.
if(frame.fp == 0) {
- frame.fp = frame.sp;
- if(frame.pc > f->entry && f->frame >= sizeof(uintptr))
- frame.fp += f->frame;
- else
- frame.fp += sizeof(uintptr);
+ frame.fp = frame.sp + runtime·funcspdelta(f, frame.pc);
+ frame.fp += sizeof(uintptr); // caller PC
}
if(frame.lr == 0)
frame.lr = ((uintptr*)frame.fp)[-1];
+ flr = runtime·findfunc(frame.lr);
// Derive size of arguments.
frame.argp = (byte*)frame.fp;
- frame.arglen = 0;
- if(f->args != ArgsSizeUnknown)
+ if(flr != nil && (i = runtime·funcarglen(flr, frame.lr)) >= 0)
+ frame.arglen = i;
+ else if(f->args != ArgsSizeUnknown)
frame.arglen = f->args;
else if(runtime·haszeroargs(f->entry))
frame.arglen = 0;
@@ -96,10 +102,10 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.arglen = stk->argsize;
else if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
frame.arglen = 2*sizeof(uintptr) + *(int32*)frame.argp;
- else if((f2 = runtime·findfunc(frame.lr)) != nil && f2->frame >= sizeof(uintptr))
- frame.arglen = f2->frame; // conservative overestimate
+ else if(flr != nil && flr->frame >= sizeof(uintptr))
+ frame.arglen = flr->frame; // conservative overestimate
else {
- runtime·printf("runtime: unknown argument frame size for %S\n", f->name);
+ runtime·printf("runtime: unknown argument frame size for %S called from %p [%S]\n", *f->name, frame.lr, flr ? *flr->name : unknown);
if(!printing)
runtime·throw("invalid stack");
}
@@ -116,7 +122,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
frame.varlen = frame.fp - sizeof(uintptr) - frame.sp;
} else {
if(f->locals > frame.fp - sizeof(uintptr) - frame.sp) {
- runtime·printf("runtime: inconsistent locals=%p frame=%p fp=%p sp=%p for %S\n", (uintptr)f->locals, (uintptr)f->frame, frame.fp, frame.sp, f->name);
+ runtime·printf("runtime: inconsistent locals=%p frame=%p fp=%p sp=%p for %S\n", (uintptr)f->locals, (uintptr)f->frame, frame.fp, frame.sp, *f->name);
runtime·throw("invalid stack");
}
frame.varp = (byte*)frame.fp - sizeof(uintptr) - f->locals;
@@ -141,7 +147,7 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
tracepc = frame.pc; // back up to CALL instruction for funcline.
if(n > 0 && frame.pc > f->entry && !waspanic)
tracepc--;
- runtime·printf("%S(", f->name);
+ runtime·printf("%S(", *f->name);
for(i = 0; i < frame.arglen/sizeof(uintptr); i++) {
if(i >= 5) {
runtime·prints(", ...");
@@ -152,7 +158,8 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
runtime·printhex(((uintptr*)frame.argp)[i]);
}
runtime·prints(")\n");
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
if(frame.pc > f->entry)
runtime·printf(" +%p", (uintptr)(frame.pc - f->entry));
if(m->throwing && gp == m->curg)
@@ -166,14 +173,12 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
skipped:
waspanic = f->entry == (uintptr)runtime·sigpanic;
- if(f->entry == (uintptr)runtime·deferproc || f->entry == (uintptr)runtime·newproc)
- frame.fp += 2*sizeof(uintptr);
-
// Do not unwind past the bottom of the stack.
- if(f->entry == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)_rt0_go)
+ if(f->entry == (uintptr)runtime·goexit || f->entry == (uintptr)runtime·mstart || f->entry == (uintptr)runtime·mcall || f->entry == (uintptr)_rt0_go)
break;
// Unwind to next frame.
+ frame.fn = flr;
frame.pc = frame.lr;
frame.lr = 0;
frame.sp = frame.fp;
@@ -189,16 +194,19 @@ runtime·gentraceback(uintptr pc0, uintptr sp0, uintptr lr0, G *gp, int32 skip,
static void
printcreatedby(G *gp)
{
+ int32 line;
+ String file;
uintptr pc, tracepc;
Func *f;
// Show what created goroutine, except main goroutine (goid 1).
if((pc = gp->gopc) != 0 && (f = runtime·findfunc(pc)) != nil && gp->goid != 1) {
- runtime·printf("created by %S\n", f->name);
+ runtime·printf("created by %S\n", *f->name);
tracepc = pc; // back up to CALL instruction for funcline.
if(pc > f->entry)
tracepc--;
- runtime·printf("\t%S:%d", f->src, runtime·funcline(f, tracepc));
+ line = runtime·funcline(f, tracepc, &file);
+ runtime·printf("\t%S:%d", file, line);
if(pc > f->entry)
runtime·printf(" +%p", (uintptr)(pc - f->entry));
runtime·printf("\n");