diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd/5l/5.out.h | 6 | ||||
-rw-r--r-- | src/cmd/5l/l.h | 4 | ||||
-rw-r--r-- | src/cmd/5l/obj.c | 3 | ||||
-rw-r--r-- | src/cmd/6l/6.out.h | 11 | ||||
-rw-r--r-- | src/cmd/6l/l.h | 4 | ||||
-rw-r--r-- | src/cmd/6l/obj.c | 3 | ||||
-rw-r--r-- | src/cmd/6l/optab.c | 9 | ||||
-rw-r--r-- | src/cmd/8l/8.out.h | 6 | ||||
-rw-r--r-- | src/cmd/8l/l.h | 4 | ||||
-rw-r--r-- | src/cmd/8l/obj.c | 3 | ||||
-rw-r--r-- | src/cmd/8l/optab.c | 5 | ||||
-rw-r--r-- | src/cmd/gc/fmt.c | 2 | ||||
-rw-r--r-- | src/cmd/ld/data.c | 28 | ||||
-rw-r--r-- | src/cmd/ld/go.c | 17 | ||||
-rw-r--r-- | src/cmd/ld/lib.c | 614 | ||||
-rw-r--r-- | src/cmd/ld/lib.h | 26 | ||||
-rw-r--r-- | src/cmd/ld/symtab.c | 14 | ||||
-rw-r--r-- | src/pkg/runtime/asm_386.s | 8 | ||||
-rw-r--r-- | src/pkg/runtime/asm_amd64.s | 8 | ||||
-rw-r--r-- | src/pkg/runtime/asm_arm.s | 8 | ||||
-rw-r--r-- | src/pkg/runtime/extern.go | 24 | ||||
-rw-r--r-- | src/pkg/runtime/mgc0.c | 9 | ||||
-rw-r--r-- | src/pkg/runtime/runtime.c | 3 | ||||
-rw-r--r-- | src/pkg/runtime/runtime.h | 30 | ||||
-rw-r--r-- | src/pkg/runtime/symtab.c | 661 | ||||
-rw-r--r-- | src/pkg/runtime/traceback_arm.c | 41 | ||||
-rw-r--r-- | src/pkg/runtime/traceback_x86.c | 54 |
27 files changed, 953 insertions, 652 deletions
diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index 95b751d94..042017c2a 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 b826cd219..c79bb79e0 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 8ecc23821..1dca38099 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 c4be74f13..5c3e80697 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 d40cc741b..97676267f 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 0fcafd701..67a791741 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 da31672d3..3aa177b27 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 f961a5bc7..fecb0852c 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 e67c6bcbd..2cf8c76b1 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 1cc0444c4..55c0e2e1b 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 c024c19b3..c08e1524a 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 cf9c3538d..ded78628e 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 c48e1392b..dc4cfcb61 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 47fdbe944..18ebf89a1 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<nelem(morename); i++) - mark(lookup(morename[i], 0)); + for(i=0; i<nelem(markextra); i++) + mark(lookup(markextra[i], 0)); for(i=0; i<ndynexp; i++) mark(dynexp[i]); @@ -794,6 +800,8 @@ Zconv(Fmt *fp) return fmtstrcpy(fp, "<nil>"); 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 18943d5f3..1447f4d65 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; i<dst->np; 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<npcdata; i++) { + if(!(havepc[i/32]>>(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 d7998a6a5..b85149f36 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 2764d50ba..5f1864d2e 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 f2265cbf0..3bf332115 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 363e680db..d7afe4b1b 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 31bbca6af..8a3597e89 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 950c0be25..cc25de155 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 a81913590..1349aa772 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 f0571f189..9b5f6c8ca 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 49503ab41..3940c3044 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 7fd700e36..a96c0ead8 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); +extern uintptr functab[]; -static uintptr -readword(byte **pp, byte *ep) -{ - byte *p; - - 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; - - 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); + int32 i, j; - // 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) +{ + ret = *f->name; + FLUSH(&ret); +} + +void +runtime·funcentry_go(Func *f, uintptr 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; - - m->nomemprof--; + 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 599f6093e..e5a475f80 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 3a9b864e8..49e1c0467 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"); |