From 5d363c6357ebcacc8ba7c24420f7cfd2e530591a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 16 Jul 2013 09:41:38 -0400 Subject: [PATCH] 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 --- src/cmd/5l/5.out.h | 6 +- src/cmd/5l/l.h | 4 +- src/cmd/5l/obj.c | 3 + src/cmd/6l/6.out.h | 11 +- src/cmd/6l/l.h | 4 +- src/cmd/6l/obj.c | 3 + src/cmd/6l/optab.c | 9 +- src/cmd/8l/8.out.h | 6 +- src/cmd/8l/l.h | 4 +- src/cmd/8l/obj.c | 3 + src/cmd/8l/optab.c | 5 +- src/cmd/gc/fmt.c | 2 + src/cmd/ld/data.c | 28 +- src/cmd/ld/go.c | 17 +- src/cmd/ld/lib.c | 614 ++++++++++++++++++++++++++++ src/cmd/ld/lib.h | 26 +- src/cmd/ld/symtab.c | 14 +- src/pkg/runtime/asm_386.s | 8 +- src/pkg/runtime/asm_amd64.s | 8 +- src/pkg/runtime/asm_arm.s | 8 +- src/pkg/runtime/extern.go | 24 +- src/pkg/runtime/mgc0.c | 9 +- src/pkg/runtime/runtime.c | 3 +- src/pkg/runtime/runtime.h | 30 +- src/pkg/runtime/symtab.c | 693 ++++++++------------------------ src/pkg/runtime/traceback_arm.c | 41 +- src/pkg/runtime/traceback_x86.c | 54 +-- 27 files changed, 969 insertions(+), 668 deletions(-) diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index 95b751d9485..042017c2a85 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -197,8 +197,12 @@ enum as AMULAWB, AUSEFIELD, - ALOCALS, ATYPE, + AFUNCDATA, + APCDATA, + + // TODO: Remove these. + ALOCALS, ANPTRS, APTRS, diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index b826cd219f3..c79bb79e050 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -172,6 +172,7 @@ struct Sym char* dynimplib; char* dynimpvers; struct Section* sect; + struct Hist* hist; // STEXT Auto* autom; @@ -282,7 +283,7 @@ enum MINSIZ = 64, NENT = 100, MAXIO = 8192, - MAXHIST = 20, /* limit of path elements for history symbols */ + MAXHIST = 40, /* limit of path elements for history symbols */ MINLC = 4, }; @@ -319,7 +320,6 @@ EXTERN char* rpath; EXTERN uint32 stroffset; EXTERN int32 symsize; EXTERN Sym* textp; -EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; EXTERN Prog zprg; EXTERN int dtype; diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index 8ecc23821ee..1dca38099fc 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -278,6 +278,7 @@ main(int argc, char *argv[]) span(); addexport(); // textaddress() functionality is handled in span() + functab(); pclntab(); symtab(); dodata(); @@ -549,6 +550,7 @@ loop: addhist(p->line, D_FILE); /* 'z' */ if(p->to.offset) addhist(p->to.offset, D_FILE1); /* 'Z' */ + savehist(p->line, p->to.offset); histfrogp = 0; goto loop; @@ -699,6 +701,7 @@ loop: p->to.offset = autosize; autosize += 4; s->type = STEXT; + s->hist = gethist(); s->text = p; s->value = pc; s->args = p->to.offset2; diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index c4be74f135b..5c3e8069703 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -759,15 +759,18 @@ enum as AAESKEYGENASSIST, APSHUFD, + APCLMULQDQ, AUSEFIELD, - ALOCALS, ATYPE, + AFUNCDATA, + APCDATA, + + // TODO: Remove these. + ALOCALS, ANPTRS, APTRS, - - APCLMULQDQ, - + ALAST }; diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index d40cc741b9c..97676267fc2 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -177,6 +177,7 @@ struct Sym char* dynimplib; char* dynimpvers; struct Section* sect; + struct Hist* hist; // for ATEXT // STEXT Auto* autom; @@ -213,7 +214,7 @@ enum STRINGSZ = 200, MINLC = 1, MAXIO = 8192, - MAXHIST = 20, /* limit of path elements for history symbols */ + MAXHIST = 40, /* limit of path elements for history symbols */ Yxxx = 0, Ynone, @@ -359,7 +360,6 @@ EXTERN int32 spsize; EXTERN Sym* symlist; EXTERN int32 symsize; EXTERN int tlsoffset; -EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; EXTERN char* paramspace; diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 0fcafd7018d..67a79174102 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -289,6 +289,7 @@ main(int argc, char *argv[]) dope(); addexport(); textaddress(); + functab(); pclntab(); symtab(); dodata(); @@ -542,6 +543,7 @@ loop: addhist(p->line, D_FILE); /* 'z' */ if(p->to.offset) addhist(p->to.offset, D_FILE1); /* 'Z' */ + savehist(p->line, p->to.offset); histfrogp = 0; goto loop; @@ -690,6 +692,7 @@ loop: s->gotype = fromgotype; } s->type = STEXT; + s->hist = gethist(); s->value = pc; s->args = p->to.offset >> 32; s->nptrs = -1; diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c index da31672d3af..3aa177b270b 100644 --- a/src/cmd/6l/optab.c +++ b/src/cmd/6l/optab.c @@ -1339,14 +1339,11 @@ Optab optab[] = { AAESKEYGENASSIST, yaes2, Pq, 0x3a,0xdf,(0) }, { APSHUFD, yaes2, Pq, 0x70,(0) }, + { APCLMULQDQ, yxshuf, Pq, 0x3a,0x44,0 }, { AUSEFIELD, ynop, Px, 0,0 }, - { ALOCALS }, - { ATYPE }, - { ANPTRS }, - { APTRS }, - - { APCLMULQDQ, yxshuf, Pq, 0x3a,0x44,0 }, + { AFUNCDATA, ynop, Px, 0,0 }, + { APCDATA, ynop, Px, 0,0 }, { AEND }, 0 diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index f961a5bc7b7..fecb0852c8e 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -578,8 +578,12 @@ enum as APSHUFB, AUSEFIELD, - ALOCALS, ATYPE, + AFUNCDATA, + APCDATA, + + // TODO: Remove these. + ALOCALS, ANPTRS, APTRS, diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index e67c6bcbda5..2cf8c76b1fc 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -159,6 +159,7 @@ struct Sym char* dynimplib; char* dynimpvers; struct Section* sect; + struct Hist* hist; // for ATEXT // STEXT Auto* autom; @@ -187,7 +188,7 @@ enum STRINGSZ = 200, MINLC = 1, MAXIO = 8192, - MAXHIST = 20, /* limit of path elements for history symbols */ + MAXHIST = 40, /* limit of path elements for history symbols */ Yxxx = 0, Ynone, @@ -313,7 +314,6 @@ EXTERN Sym* symlist; EXTERN int32 symsize; EXTERN Sym* textp; EXTERN int32 textsize; -EXTERN int version; EXTERN Prog zprg; EXTERN int dtype; EXTERN int tlsoffset; diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index 1cc0444c4ba..55c0e2e1b24 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -316,6 +316,7 @@ main(int argc, char *argv[]) span(); addexport(); textaddress(); + functab(); pclntab(); symtab(); dodata(); @@ -552,6 +553,7 @@ loop: addhist(p->line, D_FILE); /* 'z' */ if(p->to.offset) addhist(p->to.offset, D_FILE1); /* 'Z' */ + savehist(p->line, p->to.offset); histfrogp = 0; goto loop; @@ -695,6 +697,7 @@ loop: diag("%s: redefinition: %s\n%P", pn, s->name, p); } s->type = STEXT; + s->hist = gethist(); s->value = pc; s->args = p->to.offset2; s->nptrs = -1; diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index c024c19b35c..c08e1524a61 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -1001,10 +1001,9 @@ Optab optab[] = { APSHUFB, ymshufb,Pq, 0x38, 0x00 }, { AUSEFIELD, ynop, Px, 0,0 }, - { ALOCALS }, { ATYPE }, - { ANPTRS }, - { APTRS }, + { AFUNCDATA, ynop, Px, 0,0 }, + { APCDATA, ynop, Px, 0,0 }, 0 }; diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index cf9c3538d4c..ded78628e41 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -415,6 +415,8 @@ Zconv(Fmt *fp) s = sp->s; se = s + sp->len; + + // NOTE: Keep in sync with ../ld/go.c:/^Zconv. while(s < se) { n = chartorune(&r, s); s += n; diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index c48e1392b06..dc4cfcb6156 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -763,7 +763,7 @@ setuintxx(Sym *s, vlong off, uint64 v, vlong wid) s->p[off+i] = cast[inuxi8[i]]; break; } - return off; + return off+wid; } vlong @@ -800,28 +800,28 @@ adduint64(Sym *s, uint64 v) return adduintxx(s, v, 8); } -void +vlong setuint8(Sym *s, vlong r, uint8 v) { - setuintxx(s, r, v, 1); + return setuintxx(s, r, v, 1); } -void +vlong setuint16(Sym *s, vlong r, uint16 v) { - setuintxx(s, r, v, 2); + return setuintxx(s, r, v, 2); } -void +vlong setuint32(Sym *s, vlong r, uint32 v) { - setuintxx(s, r, v, 4); + return setuintxx(s, r, v, 4); } -void +vlong setuint64(Sym *s, vlong r, uint64 v) { - setuintxx(s, r, v, 8); + return setuintxx(s, r, v, 8); } vlong @@ -842,7 +842,7 @@ addaddrplus(Sym *s, Sym *t, vlong add) r->siz = PtrSize; r->type = D_ADDR; r->add = add; - return i; + return i + r->siz; } static vlong @@ -863,7 +863,7 @@ addaddrplus4(Sym *s, Sym *t, vlong add) r->siz = 4; r->type = D_ADDR; r->add = add; - return i; + return i + r->siz; } vlong @@ -884,7 +884,7 @@ addpcrelplus(Sym *s, Sym *t, vlong add) r->add = add; r->type = D_PCREL; r->siz = 4; - return i; + return i + r->siz; } vlong @@ -911,7 +911,7 @@ setaddrplus(Sym *s, vlong off, Sym *t, vlong add) r->siz = PtrSize; r->type = D_ADDR; r->add = add; - return off; + return off + r->siz; } vlong @@ -937,7 +937,7 @@ addsize(Sym *s, Sym *t) r->off = i; r->siz = PtrSize; r->type = D_SIZE; - return i; + return i + r->siz; } void diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 47fdbe9442b..18ebf89a12a 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -613,7 +613,7 @@ markflood(void) } static char* -morename[] = +markextra[] = { "runtime.morestack", "runtime.morestackx", @@ -629,6 +629,12 @@ morename[] = "runtime.morestack32", "runtime.morestack40", "runtime.morestack48", + + // on arm, lock in the div/mod helpers too + "_div", + "_divu", + "_mod", + "_modu", }; static int @@ -676,8 +682,8 @@ deadcode(void) mark(lookup(INITENTRY, 0)); if(flag_shared) mark(lookup(LIBINITENTRY, 0)); - for(i=0; i"); se = s + strlen(s); + + // NOTE: Keep in sync with ../gc/go.c:/^Zconv. while(s < se) { n = chartorune(&r, s); s += n; @@ -822,6 +830,9 @@ Zconv(Fmt *fp) fmtrune(fp, '\\'); fmtrune(fp, r); break; + case 0xFEFF: // BOM, basically disallowed in source code + fmtstrcpy(fp, "\\uFEFF"); + break; } } return 0; diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 18943d5f3d8..1447f4d658a 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -52,6 +52,14 @@ int nlibdir = 0; static int maxlibdir = 0; static int cout = -1; +// symbol version, incremented each time a file is loaded. +// version==1 is reserved for savehist. +enum +{ + HistVersion = 1, +}; +int version = HistVersion; + // Set if we see an object compiled by the host compiler that is not // from a package that is known to support internal linking mode. static int externalobj = 0; @@ -1447,6 +1455,194 @@ pclntab(void) Bflush(&bso); } +void +addvarint(Sym *s, uint32 val) +{ + int32 n; + uint32 v; + uchar *p; + + n = 0; + for(v = val; v >= 0x80; v >>= 7) + n++; + n++; + + symgrow(s, s->np+n); + + p = s->p + s->np - n; + for(v = val; v >= 0x80; v >>= 7) + *p++ = v | 0x80; + *p++ = v; +} + +// funcpctab appends to dst a pc-value table mapping the code in func to the values +// returned by valfunc parameterized by arg. The invocation of valfunc to update the +// current value is, for each p, +// +// val = valfunc(func, val, p, 0, arg); +// record val as value at p->pc; +// val = valfunc(func, val, p, 1, arg); +// +// where func is the function, val is the current value, p is the instruction being +// considered, and arg can be used to further parameterize valfunc. +void +funcpctab(Sym *dst, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg) +{ + int dbg, i, start; + int32 oldval, val, started; + uint32 delta; + vlong pc; + Prog *p; + + // To debug a specific function, uncomment second line and change name. + dbg = 0; + //dbg = strcmp(func->name, "main.main") == 0; + + debug['O'] += dbg; + + start = dst->np; + + if(debug['O']) + Bprint(&bso, "funcpctab %s -> %s [valfunc=%s]\n", func->name, dst->name, desc); + + val = -1; + oldval = val; + pc = func->value; + + if(debug['O']) + Bprint(&bso, "%6llux %6d %P\n", pc, val, func->text); + + started = 0; + for(p=func->text; p != P; p = p->link) { + // Update val. If it's not changing, keep going. + val = valfunc(func, val, p, 0, arg); + if(val == oldval && started) { + val = valfunc(func, val, p, 1, arg); + if(debug['O']) + Bprint(&bso, "%6llux %6s %P\n", p->pc, "", p); + continue; + } + + // If the pc of the next instruction is the same as the + // pc of this instruction, this instruction is not a real + // instruction. Keep going, so that we only emit a delta + // for a true instruction boundary in the program. + if(p->link && p->link->pc == p->pc) { + val = valfunc(func, val, p, 1, arg); + if(debug['O']) + Bprint(&bso, "%6llux %6s %P\n", p->pc, "", p); + continue; + } + + // The table is a sequence of (value, pc) pairs, where each + // pair states that the given value is in effect from the current position + // up to the given pc, which becomes the new current position. + // To generate the table as we scan over the program instructions, + // we emit a "(value" when pc == func->value, and then + // each time we observe a change in value we emit ", pc) (value". + // When the scan is over, we emit the closing ", pc)". + // + // The table is delta-encoded. The value deltas are signed and + // transmitted in zig-zag form, where a complement bit is placed in bit 0, + // and the pc deltas are unsigned. Both kinds of deltas are sent + // as variable-length little-endian base-128 integers, + // where the 0x80 bit indicates that the integer continues. + + if(debug['O']) + Bprint(&bso, "%6llux %6d %P\n", p->pc, val, p); + + if(!started) + started = 1; + else { + addvarint(dst, (p->pc - pc) / MINLC); + pc = p->pc; + } + delta = val - oldval; + if(delta>>31) + delta = 1 | ~(delta<<1); + else + delta <<= 1; + addvarint(dst, delta); + oldval = val; + started = 1; + val = valfunc(func, val, p, 1, arg); + } + + if(started) { + if(debug['O']) + Bprint(&bso, "%6llux done\n", func->value+func->size); + addvarint(dst, (func->value+func->size - pc) / MINLC); + addvarint(dst, 0); // terminator + } + + if(debug['O']) { + Bprint(&bso, "wrote %d bytes\n", dst->np - start); + for(i=start; inp; i++) + Bprint(&bso, " %02ux", dst->p[i]); + Bprint(&bso, "\n"); + } + + debug['O'] -= dbg; +} + +// pctofileline computes either the file number (arg == 0) +// or the line number (arg == 1) to use at p. +// Because p->lineno applies to p, phase == 0 (before p) +// takes care of the update. +static int32 +pctofileline(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) +{ + int32 f, l; + + if(p->as == ATEXT || p->as == ANOP || p->as == AUSEFIELD || p->line == 0 || phase == 1) + return oldval; + getline(sym->hist, p->line, &f, &l); + if(f == 0) { + // print("getline failed for %s %P\n", cursym->name, p); + return oldval; + } + if(arg == 0) + return f; + return l; +} + +// pctospadj computes the sp adjustment in effect. +// It is oldval plus any adjustment made by p itself. +// The adjustment by p takes effect only after p, so we +// apply the change during phase == 1. +static int32 +pctospadj(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) +{ + USED(arg); + + if(oldval == -1) // starting + oldval = 0; + if(phase == 0) + return oldval; + if(oldval + p->spadj < -10000 || oldval + p->spadj > 1000000000) { + diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj); + errorexit(); + } + return oldval + p->spadj; +} + +// pctopcdata computes the pcdata value in effect at p. +// A PCDATA instruction sets the value in effect at future +// non-PCDATA instructions. +// Since PCDATA instructions have no width in the final code, +// it does not matter which phase we use for the update. +static int32 +pctopcdata(Sym *sym, int32 oldval, Prog *p, int32 phase, int32 arg) +{ + if(phase == 0 || p->as != APCDATA || p->from.offset != arg) + return oldval; + if((int32)p->to.offset != p->to.offset) { + diag("overflow in PCDATA instruction: %P", p); + errorexit(); + } + return p->to.offset; +} + #define LOG 5 void mkfwd(void) @@ -2001,3 +2197,421 @@ erealloc(void *p, long n) } return p; } + +// Saved history stacks encountered while reading archives. +// Keeping them allows us to answer virtual lineno -> file:line +// queries. +// +// The history stack is a complex data structure, described best at the +// bottom of http://plan9.bell-labs.com/magic/man2html/6/a.out. +// One of the key benefits of interpreting it here is that the runtime +// does not have to. Perhaps some day the compilers could generate +// a simpler linker input too. + +struct Hist +{ + int32 line; + int32 off; + Sym *file; +}; + +static Hist *histcopy; +static Hist *hist; +static int32 nhist; +static int32 maxhist; +static int32 histdepth; +static int32 nhistfile; +static Sym *filesyms; + +// savehist processes a single line, off history directive +// found in the input object file. +void +savehist(int32 line, int32 off) +{ + char tmp[1024]; + Sym *file; + Hist *h; + + tmp[0] = '\0'; + copyhistfrog(tmp, sizeof tmp); + + if(tmp[0]) { + file = lookup(tmp, HistVersion); + if(file->type != SFILEPATH) { + file->value = ++nhistfile; + file->type = SFILEPATH; + file->next = filesyms; + filesyms = file; + } + } else + file = nil; + + if(file != nil && line == 1 && off == 0) { + // start of new stack + if(histdepth != 0) { + diag("history stack phase error: unexpected start of new stack depth=%d file=%s", histdepth, tmp); + errorexit(); + } + nhist = 0; + histcopy = nil; + } + + if(nhist >= maxhist) { + if(maxhist == 0) + maxhist = 1; + maxhist *= 2; + hist = erealloc(hist, maxhist*sizeof hist[0]); + } + h = &hist[nhist++]; + h->line = line; + h->off = off; + h->file = file; + + if(file != nil) { + if(off == 0) + histdepth++; + } else { + if(off != 0) { + diag("history stack phase error: bad offset in pop"); + errorexit(); + } + histdepth--; + } +} + +// gethist returns the history stack currently in effect. +// The result is valid indefinitely. +Hist* +gethist(void) +{ + if(histcopy == nil) { + if(nhist == 0) + return nil; + histcopy = mal((nhist+1)*sizeof hist[0]); + memmove(histcopy, hist, nhist*sizeof hist[0]); + histcopy[nhist].line = -1; + } + return histcopy; +} + +typedef struct Hstack Hstack; +struct Hstack +{ + Hist *h; + int delta; +}; + +// getline sets *f to the file number and *l to the line number +// of the virtual line number line according to the history stack h. +void +getline(Hist *h, int32 line, int32 *f, int32 *l) +{ + Hstack stk[100]; + int nstk, start; + Hist *top, *h0; + static Hist *lasth; + static int32 laststart, lastend, lastdelta, lastfile; + + h0 = h; + *f = 0; + *l = 0; + start = 0; + if(h == nil || line == 0) { + print("%s: getline: h=%p line=%d\n", cursym->name, h, line); + return; + } + + // Cache span used during last lookup, so that sequential + // translation of line numbers in compiled code is efficient. + if(!debug['O'] && lasth == h && laststart <= line && line < lastend) { + *f = lastfile; + *l = line - lastdelta; + return; + } + + if(debug['O']) + print("getline %d laststart=%d lastend=%d\n", line, laststart, lastend); + + nstk = 0; + for(; h->line != -1; h++) { + if(debug['O']) + print("\t%s %d %d\n", h->file ? h->file->name : "?", h->line, h->off); + + if(h->line > line) { + if(nstk == 0) { + diag("history stack phase error: empty stack at line %d", (int)line); + errorexit(); + } + top = stk[nstk-1].h; + lasth = h; + lastfile = top->file->value; + laststart = start; + lastend = h->line; + lastdelta = stk[nstk-1].delta; + *f = lastfile; + *l = line - lastdelta; + if(debug['O']) + print("\tgot %d %d [%d %d %d]\n", *f, *l, laststart, lastend, lastdelta); + return; + } + if(h->file == nil) { + // pop included file + if(nstk == 0) { + diag("history stack phase error: stack underflow"); + errorexit(); + } + nstk--; + if(nstk > 0) + stk[nstk-1].delta += h->line - stk[nstk].h->line; + start = h->line; + } else if(h->off == 0) { + // push included file + if(nstk >= nelem(stk)) { + diag("history stack phase error: stack overflow"); + errorexit(); + } + start = h->line; + stk[nstk].h = h; + stk[nstk].delta = h->line - 1; + nstk++; + } else { + // #line directive + if(nstk == 0) { + diag("history stack phase error: stack underflow"); + errorexit(); + } + stk[nstk-1].h = h; + stk[nstk-1].delta = h->line - h->off; + start = h->line; + } + if(debug['O']) + print("\t\tnstk=%d delta=%d\n", nstk, stk[nstk].delta); + } + + diag("history stack phase error: cannot find line for %d", line); + nstk = 0; + for(h = h0; h->line != -1; h++) { + print("\t%d %d %s\n", h->line, h->off, h->file ? h->file->name : ""); + if(h->file == nil) + nstk--; + else if(h->off == 0) + nstk++; + } +} + +// defgostring returns a symbol for the Go string containing text. +Sym* +defgostring(char *text) +{ + char *p; + Sym *s; + int32 n; + + n = strlen(text); + p = smprint("go.string.\"%Z\"", text); + s = lookup(p, 0); + if(s->size == 0) { + s->type = SGOSTRING; + s->reachable = 1; + s->size = 2*PtrSize+n; + symgrow(s, 2*PtrSize+n); + setaddrplus(s, 0, s, 2*PtrSize); + setuintxx(s, PtrSize, n, PtrSize); + memmove(s->p+2*PtrSize, text, n); + } + s->reachable = 1; + return s; +} + +// addpctab appends to f a pc-value table, storing its offset at off. +// The pc-value table is for func and reports the value of valfunc +// parameterized by arg. +static int32 +addpctab(Sym *f, int32 off, Sym *func, char *desc, int32 (*valfunc)(Sym*, int32, Prog*, int32, int32), int32 arg) +{ + int32 start; + + start = f->np; + funcpctab(f, func, desc, valfunc, arg); + if(start == f->np) { + // no table + return setuint32(f, off, 0); + } + if((int32)start > (int32)f->np) { + diag("overflow adding pc-table: symbol too large"); + errorexit(); + } + return setuint32(f, off, start); +} + +// functab initializes the functab and filetab symbols with +// runtime function and file name information. +void +functab(void) +{ + Prog *p; + int32 i, n, start; + uint32 *havepc, *havefunc; + Sym *ftab, *f; + int32 npcdata, nfuncdata, off, end; + char *q; + + ftab = lookup("functab", 0); + ftab->type = SRODATA; + ftab->reachable = 1; + + if(debug['s']) + return; + + adduintxx(ftab, 0, PtrSize); + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + q = smprint("go.func.%s", cursym->name); + f = lookup(q, cursym->version); + f->type = SRODATA; + f->reachable = 1; + free(q); + + addaddrplus(ftab, cursym, 0); + addaddrplus(ftab, f, 0); + + npcdata = 0; + nfuncdata = 0; + for(p = cursym->text; p != P; p = p->link) { + if(p->as == APCDATA && p->from.offset >= npcdata) + npcdata = p->from.offset+1; + if(p->as == AFUNCDATA && p->from.offset >= nfuncdata) + nfuncdata = p->from.offset+1; + } + + off = 0; + // fixed size of struct, checked below + end = 2*PtrSize + 5*4 + 5*4 + npcdata*4 + nfuncdata*PtrSize; + if(nfuncdata > 0 && (end&(PtrSize-1))) + end += 4; + symgrow(f, end); + + // name *string + off = setaddr(f, off, defgostring(cursym->name)); + + // entry uintptr + off = setaddr(f, off, cursym); + + // args int32 + // TODO: Move into funcinfo. + if(cursym->text == nil || (cursym->text->textflag & NOSPLIT) && cursym->args == 0 && cursym->nptrs < 0) { + // This might be a vararg function and have no + // predetermined argument size. This check is + // approximate and will also match 0 argument + // nosplit functions compiled by 6c. + off = setuint32(f, off, ArgsSizeUnknown); + } else + off = setuint32(f, off, cursym->args); + + // locals int32 + // TODO: Move into funcinfo. + off = setuint32(f, off, cursym->locals); + + // frame int32 + // TODO: Remove entirely. The pcsp table is more precise. + // This is only used by a fallback case during stack walking + // when a called function doesn't have argument information. + // We need to make sure everything has argument information + // and then remove this. + if(cursym->text == nil) + off = setuint32(f, off, 0); + else + off = setuint32(f, off, (uint32)cursym->text->to.offset+PtrSize); + + // TODO: Move into funcinfo. + // ptrsoff, ptrslen int32 + start = f->np; + for(i = 0; i < cursym->nptrs; i += 32) + adduint32(f, cursym->ptrs[i/32]); + off = setuint32(f, off, start); + off = setuint32(f, off, (f->np - start)/4); + + // pcsp table (offset int32) + off = addpctab(f, off, cursym, "pctospadj", pctospadj, 0); + + // pcfile table (offset int32) + off = addpctab(f, off, cursym, "pctofileline file", pctofileline, 0); + + // pcln table (offset int32) + off = addpctab(f, off, cursym, "pctofileline line", pctofileline, 1); + + // npcdata int32 + off = setuint32(f, off, npcdata); + + // nfuncdata int32 + off = setuint32(f, off, nfuncdata); + + // tabulate which pc and func data we have. + n = ((npcdata+31)/32 + (nfuncdata+31)/32)*4; + havepc = mal(n); + havefunc = havepc + (npcdata+31)/32; + for(p = cursym->text; p != P; p = p->link) { + if(p->as == AFUNCDATA) { + if((havefunc[p->from.offset/32]>>(p->from.offset%32))&1) + diag("multiple definitions for FUNCDATA $%d", i); + havefunc[p->from.offset/32] |= 1<<(p->from.offset%32); + } + if(p->as == APCDATA) + havepc[p->from.offset/32] |= 1<<(p->from.offset%32); + } + + // pcdata. + for(i=0; i>(i%32))&1) { + off = setuint32(f, off, 0); + continue; + } + off = addpctab(f, off, cursym, "pctopcdata", pctopcdata, i); + } + + unmal(havepc, n); + + // funcdata, must be pointer-aligned and we're only int32-aligned. + // Unlike pcdata, can gather in a single pass. + // Missing funcdata will be 0 (nil pointer). + if(nfuncdata > 0) { + if(off&(PtrSize-1)) + off += 4; + for(p = cursym->text; p != P; p = p->link) { + if(p->as == AFUNCDATA) { + i = p->from.offset; + if(p->to.type == D_CONST) + setuintxx(f, off+PtrSize*i, p->to.offset, PtrSize); + else + setaddrplus(f, off+PtrSize*i, p->to.sym, p->to.offset); + } + } + off += nfuncdata*PtrSize; + } + + if(off != end) { + diag("bad math in functab: off=%d but end=%d (npcdata=%d nfuncdata=%d)", off, end, npcdata, nfuncdata); + errorexit(); + } + + f->size = f->np; + + // Final entry of table is just end pc. + if(cursym->next == nil) { + addaddrplus(ftab, cursym, cursym->size); + adduintxx(ftab, 0, PtrSize); + } + } + + setuintxx(ftab, 0, (ftab->np-PtrSize)/(2*PtrSize) - 1, PtrSize); + ftab->size = ftab->np; + + ftab = lookup("filetab", 0); + ftab->type = SRODATA; + ftab->reachable = 1; + symgrow(ftab, (nhistfile+1)*PtrSize); + setuintxx(ftab, 0, nhistfile+1, PtrSize); + for(f = filesyms; f != S; f = f->next) + setaddr(ftab, f->value*PtrSize, defgostring(f->name)); + ftab->size = ftab->np; +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index d7998a6a5ab..b85149f3609 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -41,10 +41,12 @@ enum STYPE, SSTRING, SGOSTRING, + SGOFUNC, SRODATA, + SFUNCTAB, STYPELINK, - SSYMTAB, - SPCLNTAB, + SSYMTAB, // TODO: move to unmapped section + SPCLNTAB, // TODO: move to unmapped section SELFROSECT, /* writable, non-executable */ @@ -67,6 +69,7 @@ enum SMACHOINDIRECTPLT, SMACHOINDIRECTGOT, SFILE, + SFILEPATH, SCONST, SDYNIMPORT, SHOSTOBJ, @@ -129,9 +132,14 @@ struct Section uvlong rellen; }; +typedef struct Hist Hist; + +#pragma incomplete struct Hist + extern char symname[]; extern char **libdir; extern int nlibdir; +extern int version; EXTERN char* INITENTRY; EXTERN char* thestring; @@ -194,6 +202,9 @@ void addlibpath(char *srcref, char *objref, char *file, char *pkg); Section* addsection(Segment*, char*, int); void copyhistfrog(char *buf, int nbuf); void addhist(int32 line, int type); +void savehist(int32 line, int32 off); +Hist* gethist(void); +void getline(Hist*, int32 line, int32 *f, int32 *l); void asmlc(void); void histtoauto(void); void collapsefrog(Sym *s); @@ -216,6 +227,7 @@ void objfile(char *file, char *pkg); void libinit(void); void pclntab(void); void symtab(void); +void functab(void); void Lflag(char *arg); void usage(void); void adddynrel(Sym*, Reloc*); @@ -251,10 +263,11 @@ vlong addpcrelplus(Sym*, Sym*, vlong); vlong addsize(Sym*, Sym*); vlong setaddrplus(Sym*, vlong, Sym*, vlong); vlong setaddr(Sym*, vlong, Sym*); -void setuint8(Sym*, vlong, uint8); -void setuint16(Sym*, vlong, uint16); -void setuint32(Sym*, vlong, uint32); -void setuint64(Sym*, vlong, uint64); +vlong setuint8(Sym*, vlong, uint8); +vlong setuint16(Sym*, vlong, uint16); +vlong setuint32(Sym*, vlong, uint32); +vlong setuint64(Sym*, vlong, uint64); +vlong setuintxx(Sym*, vlong, uint64, vlong); void asmsym(void); void asmelfsym(void); void asmplan9sym(void); @@ -284,6 +297,7 @@ void hostobjs(void); void hostlink(void); char* estrdup(char*); void* erealloc(void*, long); +Sym* defgostring(char*); int pathchar(void); void* mal(uint32); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 2764d50ba45..5f1864d2e4f 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -466,7 +466,8 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) void symtab(void) { - Sym *s, *symtype, *symtypelink, *symgostring; + Sym *s, *symtype, *symtypelink, *symgostring, *symgofunc; + dosymtype(); // Define these so that they'll get put into the symbol table. @@ -519,6 +520,12 @@ symtab(void) s->reachable = 1; symgostring = s; + s = lookup("go.func.*", 0); + s->type = SGOFUNC; + s->size = 0; + s->reachable = 1; + symgofunc = s; + symtypelink = lookup("typelink", 0); symt = lookup("symtab", 0); @@ -548,6 +555,11 @@ symtab(void) s->hide = 1; s->outer = symgostring; } + if(strncmp(s->name, "go.func.", 8) == 0) { + s->type = SGOFUNC; + s->hide = 1; + s->outer = symgofunc; + } } if(debug['s']) diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s index f2265cbf014..3bf3321156c 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 363e680db92..d7afe4b1b19 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 31bbca6afaa..8a3597e892f 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 950c0be2575..cc25de1554a 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 a8191359019..1349aa7726f 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 f0571f18995..9b5f6c8ca35 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 49503ab41ba..3940c304473 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 7fd700e36ac..a96c0ead809 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,423 +11,82 @@ #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 uintptr functab[]; -extern void main·main(void); +static Ftab *ftab; +static uintptr nftab; +extern String *filetab[]; +static uintptr nfiletab; + +static String end = { (uint8*)"end", 3 }; + +void +runtime·symtabinit(void) +{ + int32 i, j; + + ftab = (Ftab*)(functab+1); + nftab = functab[0]; + + for(i=0; i 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]; +} + +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; +} static uintptr -readword(byte **pp, byte *ep) +funcdata(Func *f, int32 i) { - byte *p; + byte *p; - p = *pp; - if(ep - p < sizeof(void*)) { - *pp = ep; + if(i < 0 || i >= f->nfuncdata) return 0; - } - *pp = p + sizeof(void*); - - // 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]); + p = (byte*)&f->nfuncdata + 4 + f->npcdata*4; + if(sizeof(void*) == 8 && ((uintptr)p & 4)) + p += 4; + return ((uintptr*)p)[i]; } -// Walk over symtab, calling fn(&s) for each symbol. -static void -walksymtab(void (*fn)(Sym*)) -{ - byte *p, *ep, *q; - Sym s; - int32 widevalue, havetype, shift; - - 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)<= ep) - break; - s.value |= (uintptr)p[0]< 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; - } - - fn(&s); - } -} - -// 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) -{ - Func *f; - uintgo cap; - - 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; - } - break; - } - fname[sym->value] = sym->name; - break; - } -} - -// 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) -{ - int32 n, len; - byte *p, *ep, *q; - - if(nbuf <= 0) - 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; - } - } - } -} - -// Interpret pc/ln table, saving the subpiece for each func. -static void -splitpcln(void) -{ - int32 line; - uintptr pc; - byte *p, *ep; - Func *f, *ef; - int32 pcquant; - - if(pclntab == epclntab || nfunc == 0) - return; - - switch(thechar) { - case '5': - pcquant = 4; - break; - default: // 6, 8 - pcquant = 1; - break; - } - - // pc/ln table bounds - p = pclntab; - ep = epclntab; - - f = func; - ef = func + nfunc; - pc = func[0].entry; // text base - f->pcln.array = p; - f->pc0 = pc; - line = 0; - 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++; - 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; - } -} - - -// Return actual file line number for targetpc in func f. +// Return associated data value 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) +static int32 +pcvalue(Func *f, int32 off, uintptr targetpc) { - byte *p, *ep; + byte *p; uintptr pc; - int32 line; - int32 pcquant; + int32 value, vdelta, pcshift; + uint32 uvdelta, pcdelta; enum { debug = 0 @@ -480,125 +94,138 @@ runtime·funcline(Func *f, uintptr targetpc) switch(thechar) { case '5': - pcquant = 4; + pcshift = 2; break; default: // 6, 8 - pcquant = 1; + pcshift = 0; break; } - p = f->pcln.array; - ep = p + f->pcln.len; - pc = f->pc0; - line = f->ln0; + // 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; + 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); + runtime·printf("pcvalue start f=%S [%p] pc=%p targetpc=%p value=%d tab=%p\n", + *f->name, f, pc, targetpc, value, p); + 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) + uvdelta = readvarint(&p); + if(uvdelta == 0 && pc != f->entry) break; - - // ... 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++; + if(uvdelta&1) + uvdelta = ~(uvdelta>>1); 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); - - // PC increments implicitly on each iteration. - pc += pcquant; + 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 }; + +int32 +runtime·funcline(Func *f, uintptr targetpc, String *file) +{ + int32 line; + int32 fileno; + + *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; } +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; +} + +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); +} + +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[]; + ret = *f->name; + FLUSH(&ret); +} - 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; - - 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 599f6093eb7..e5a475f80fb 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 3a9b864e89a..49e1c0467f3 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");