diff options
Diffstat (limited to 'src/pkg/runtime/symtab.c')
-rw-r--r-- | src/pkg/runtime/symtab.c | 256 |
1 files changed, 206 insertions, 50 deletions
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c index f29276bd7..d7221c476 100644 --- a/src/pkg/runtime/symtab.c +++ b/src/pkg/runtime/symtab.c @@ -2,20 +2,59 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Runtime symbol table access. Work in progress. -// The Plan 9 symbol table is not in a particularly convenient form. -// The routines here massage it into a more usable form; eventually -// we'll change 6l to do this for us, but it is easier to experiment -// here than to change 6l and all the other tools. +// Runtime symbol table parsing. // -// The symbol table also needs to be better integrated with the type -// strings table in the future. This is just a quick way to get started -// and figure out exactly what we want. +// 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. #include "runtime.h" #include "defs_GOOS_GOARCH.h" #include "os_GOOS.h" #include "arch_GOARCH.h" +#include "malloc.h" extern byte pclntab[], epclntab[], symtab[], esymtab[]; @@ -28,24 +67,100 @@ struct Sym // byte *gotype; }; +static uintptr mainoffset; + +// A dynamically allocated string containing multiple substrings. +// Individual strings are slices of hugestring. +static String hugestring; +static int32 hugestring_len; + +extern void main·main(void); + +static uintptr +readword(byte **pp, byte *ep) +{ + byte *p; + + p = *pp; + if(ep - p < sizeof(void*)) { + *pp = ep; + return 0; + } + *pp = p + sizeof(void*); + + // Hairy, but only one of these four cases gets compiled. + if(sizeof(void*) == 8) { + if(BigEndian) { + return ((uint64)p[0]<<56) | ((uint64)p[1]<<48) | ((uint64)p[2]<<40) | ((uint64)p[3]<<32) | + ((uint64)p[4]<<24) | ((uint64)p[5]<<16) | ((uint64)p[6]<<8) | ((uint64)p[7]); + } + return ((uint64)p[7]<<56) | ((uint64)p[6]<<48) | ((uint64)p[5]<<40) | ((uint64)p[4]<<32) | + ((uint64)p[3]<<24) | ((uint64)p[2]<<16) | ((uint64)p[1]<<8) | ((uint64)p[0]); + } + if(BigEndian) { + return ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]); + } + return ((uint32)p[3]<<24) | ((uint32)p[2]<<16) | ((uint32)p[1]<<8) | ((uint32)p[0]); +} + // Walk over symtab, calling fn(&s) for each symbol. static void walksymtab(void (*fn)(Sym*)) { byte *p, *ep, *q; Sym s; + int32 widevalue, havetype, shift; p = symtab; ep = esymtab; + + // Table must begin with correct magic number. + if(ep - p < 8 || p[4] != 0x00 || p[5] != 0x00 || p[6] != 0x00 || p[7] != sizeof(void*)) + return; + if(BigEndian) { + if(p[0] != 0xff || p[1] != 0xff || p[2] != 0xff || p[3] != 0xfd) + return; + } else { + if(p[0] != 0xfd || p[1] != 0xff || p[2] != 0xff || p[3] != 0xff) + return; + } + p += 8; + while(p < ep) { - if(p + 7 > ep) - break; - s.value = ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]); + 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); - if(!(p[4]&0x80)) + // Name. + if(ep - p < 2) break; - s.symtype = p[4] & ~0x80; - p += 5; + s.name = p; if(s.symtype == 'z' || s.symtype == 'Z') { // path reference string - skip first byte, @@ -65,7 +180,7 @@ walksymtab(void (*fn)(Sym*)) break; p = q+1; } - p += 4; // go type + fn(&s); } } @@ -80,12 +195,13 @@ static int32 nfname; static uint32 funcinit; static Lock funclock; +static uintptr lastvalue; static void dofunc(Sym *sym) { Func *f; - + switch(sym->symtype) { case 't': case 'T': @@ -93,6 +209,11 @@ dofunc(Sym *sym) case 'L': if(runtime·strcmp(sym->name, (byte*)"etext") == 0) break; + if(sym->value < lastvalue) { + runtime·printf("symbols out of order: %p before %p\n", lastvalue, sym->value); + runtime·throw("malformed symbol table"); + } + lastvalue = sym->value; if(func == nil) { nfunc++; break; @@ -104,24 +225,24 @@ dofunc(Sym *sym) f->frame = -sizeof(uintptr); break; case 'm': - if(nfunc > 0 && func != nil) - func[nfunc-1].frame += sym->value; - break; - case 'p': - if(nfunc > 0 && func != nil) { - f = &func[nfunc-1]; - // args counts 32-bit words. - // sym->value is the arg's offset. - // don't know width of this arg, so assume it is 64 bits. - if(f->args < sym->value/4 + 2) - f->args = sym->value/4 + 2; + 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 { + runtime·printf("invalid 'm' symbol named '%s'\n", sym->name); + runtime·throw("mangled symbol table"); } break; case 'f': if(fname == nil) { if(sym->value >= nfname) { if(sym->value >= 0x10000) { - runtime·printf("invalid symbol file index %p\n", sym->value); + runtime·printf("runtime: invalid symbol file index %p\n", sym->value); runtime·throw("mangled symbol table"); } nfname = sym->value+1; @@ -135,14 +256,15 @@ dofunc(Sym *sym) // put together the path name for a z entry. // the f entries have been accumulated into fname already. -static void +// returns the length of the path name. +static int32 makepath(byte *buf, int32 nbuf, byte *path) { int32 n, len; byte *p, *ep, *q; if(nbuf <= 0) - return; + return 0; p = buf; ep = buf + nbuf; @@ -163,6 +285,26 @@ makepath(byte *buf, int32 nbuf, byte *path) runtime·memmove(p, q, len+1); p += len; } + return p - buf; +} + +// appends p to hugestring +static String +gostringn(byte *p, int32 l) +{ + String s; + + if(l == 0) + return runtime·emptystring; + if(hugestring.str == nil) { + hugestring_len += l; + return runtime·emptystring; + } + s.str = hugestring.str + hugestring.len; + s.len = l; + hugestring.len += s.len; + runtime·memmove(s.str, p, l); + return s; } // walk symtab accumulating path names for use by pc/ln table. @@ -181,11 +323,13 @@ dosrcline(Sym *sym) static int32 incstart; static int32 nfunc, nfile, nhist; Func *f; - int32 i; + int32 i, l; switch(sym->symtype) { case 't': case 'T': + if(hugestring.str == nil) + break; if(runtime·strcmp(sym->name, (byte*)"etext") == 0) break; f = &func[nfunc++]; @@ -200,23 +344,23 @@ dosrcline(Sym *sym) case 'z': if(sym->value == 1) { // entry for main source file for a new object. - makepath(srcbuf, sizeof srcbuf, sym->name+1); + l = makepath(srcbuf, sizeof srcbuf, sym->name+1); nhist = 0; nfile = 0; if(nfile == nelem(files)) return; - files[nfile].srcstring = runtime·gostring(srcbuf); + files[nfile].srcstring = gostringn(srcbuf, l); files[nfile].aline = 0; files[nfile++].delta = 0; } else { // push or pop of included file. - makepath(srcbuf, sizeof srcbuf, sym->name+1); + l = makepath(srcbuf, sizeof srcbuf, sym->name+1); if(srcbuf[0] != '\0') { if(nhist++ == 0) incstart = sym->value; if(nhist == 0 && nfile < nelem(files)) { // new top-level file - files[nfile].srcstring = runtime·gostring(srcbuf); + files[nfile].srcstring = gostringn(srcbuf, l); files[nfile].aline = sym->value; // this is "line 0" files[nfile++].delta = sym->value - 1; @@ -275,7 +419,7 @@ splitpcln(void) line += *p++; else line -= *p++ - 64; - + // pc, line now match. // Because the state machine begins at pc==entry and line==0, // it can happen - just at the beginning! - that the update may @@ -297,7 +441,7 @@ splitpcln(void) 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 + // 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; @@ -323,11 +467,11 @@ runtime·funcline(Func *f, uintptr targetpc) uintptr pc; int32 line; int32 pcquant; - + enum { debug = 0 }; - + switch(thechar) { case '5': pcquant = 4; @@ -354,7 +498,7 @@ runtime·funcline(Func *f, uintptr targetpc) 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) @@ -382,7 +526,7 @@ runtime·funcline(Func *f, uintptr targetpc) } void -runtime·funcline_go(Func *f, uintptr targetpc, String retfile, int32 retline) +runtime·funcline_go(Func *f, uintptr targetpc, String retfile, intgo retline) { retfile = f->src; retline = runtime·funcline(f, targetpc); @@ -406,20 +550,30 @@ buildfuncs(void) // count funcs, fnames nfunc = 0; nfname = 0; + lastvalue = 0; walksymtab(dofunc); - // initialize tables - func = runtime·mal((nfunc+1)*sizeof func[0]); + // Initialize tables. + // Can use FlagNoPointers - all pointers either point into sections of the executable + // or point into hugestring. + func = runtime·mallocgc((nfunc+1)*sizeof func[0], FlagNoPointers, 0, 1); func[nfunc].entry = (uint64)etext; - fname = runtime·mal(nfname*sizeof fname[0]); + fname = runtime·mallocgc(nfname*sizeof fname[0], FlagNoPointers, 0, 1); nfunc = 0; + lastvalue = 0; walksymtab(dofunc); // split pc/ln table by func splitpcln(); // record src file and line info for each func - walksymtab(dosrcline); + walksymtab(dosrcline); // pass 1: determine hugestring_len + hugestring.str = runtime·mallocgc(hugestring_len, FlagNoPointers, 0, 0); + hugestring.len = 0; + walksymtab(dosrcline); // pass 2: fill and use hugestring + + if(hugestring.len != hugestring_len) + runtime·throw("buildfunc: problem in initialization procedure"); m->nomemprof--; } @@ -482,7 +636,7 @@ static bool hasprefix(String s, int8 *p) { int32 i; - + for(i=0; i<s.len; i++) { if(p[i] == 0) return 1; @@ -496,7 +650,7 @@ static bool contains(String s, int8 *p) { int32 i; - + if(p[0] == 0) return 1; for(i=0; i<s.len; i++) { @@ -509,11 +663,13 @@ contains(String s, int8 *p) } bool -runtime·showframe(Func *f) +runtime·showframe(Func *f, bool current) { static int32 traceback = -1; - + + if(current && m->throwing > 0) + return 1; if(traceback < 0) traceback = runtime·gotraceback(); - return traceback > 1 || contains(f->name, ".") && !hasprefix(f->name, "runtime."); + return traceback > 1 || f != nil && contains(f->name, ".") && !hasprefix(f->name, "runtime."); } |