summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmd/5l/5.out.h6
-rw-r--r--src/cmd/5l/l.h4
-rw-r--r--src/cmd/5l/obj.c3
-rw-r--r--src/cmd/6l/6.out.h11
-rw-r--r--src/cmd/6l/l.h4
-rw-r--r--src/cmd/6l/obj.c3
-rw-r--r--src/cmd/6l/optab.c9
-rw-r--r--src/cmd/8l/8.out.h6
-rw-r--r--src/cmd/8l/l.h4
-rw-r--r--src/cmd/8l/obj.c3
-rw-r--r--src/cmd/8l/optab.c5
-rw-r--r--src/cmd/gc/fmt.c2
-rw-r--r--src/cmd/ld/data.c28
-rw-r--r--src/cmd/ld/go.c17
-rw-r--r--src/cmd/ld/lib.c614
-rw-r--r--src/cmd/ld/lib.h26
-rw-r--r--src/cmd/ld/symtab.c14
-rw-r--r--src/pkg/runtime/asm_386.s8
-rw-r--r--src/pkg/runtime/asm_amd64.s8
-rw-r--r--src/pkg/runtime/asm_arm.s8
-rw-r--r--src/pkg/runtime/extern.go24
-rw-r--r--src/pkg/runtime/mgc0.c9
-rw-r--r--src/pkg/runtime/runtime.c3
-rw-r--r--src/pkg/runtime/runtime.h30
-rw-r--r--src/pkg/runtime/symtab.c661
-rw-r--r--src/pkg/runtime/traceback_arm.c41
-rw-r--r--src/pkg/runtime/traceback_x86.c54
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");