| // Copyright 2009 The Go Authors. All rights reserved. |
| // 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. |
| // |
| // 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. |
| |
| #include "runtime.h" |
| |
| // TODO(rsc): Move this *under* the text segment. |
| // Then define names for these addresses instead of hard-coding magic ones. |
| #ifdef _64BIT |
| #define SYMCOUNTS ((int32*)(0x99LL<<32)) // known to 6l |
| #define SYMDATA ((byte*)(0x99LL<<32) + 8) |
| #else |
| #define SYMCOUNTS ((int32*)(0x99LL<<24)) // known to 8l |
| #define SYMDATA ((byte*)(0x99LL<<24) + 8) |
| #endif |
| |
| |
| // Return a pointer to a byte array containing the symbol table segment. |
| void |
| sysยทsymdat(Array *symtab, Array *pclntab) |
| { |
| Array *a; |
| int32 *v; |
| |
| v = SYMCOUNTS; |
| |
| a = mal(sizeof *a); |
| a->nel = v[0]; |
| a->cap = a->nel; |
| a->array = SYMDATA; |
| symtab = a; |
| FLUSH(&symtab); |
| |
| a = mal(sizeof *a); |
| a->nel = v[1]; |
| a->cap = a->nel; |
| a->array = SYMDATA + v[0]; |
| pclntab = a; |
| FLUSH(&pclntab); |
| } |
| |
| typedef struct Sym Sym; |
| struct Sym |
| { |
| uintptr value; |
| byte symtype; |
| byte *name; |
| byte *gotype; |
| }; |
| |
| // Walk over symtab, calling fn(&s) for each symbol. |
| static void |
| walksymtab(void (*fn)(Sym*)) |
| { |
| int32 *v; |
| byte *p, *ep, *q; |
| Sym s; |
| |
| v = SYMCOUNTS; |
| p = SYMDATA; |
| ep = p + v[0]; |
| 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]); |
| if(!(p[4]&0x80)) |
| break; |
| s.symtype = p[4] & ~0x80; |
| p += 5; |
| 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 = mchr(p, '\0', ep); |
| if(q == nil) |
| break; |
| p = q+1; |
| } |
| q = mchr(p, '\0', ep); |
| if(q == nil) |
| break; |
| s.gotype = p; |
| p = q+1; |
| fn(&s); |
| } |
| } |
| |
| // Symtab walker; accumulates info about functions. |
| |
| static Func *func; |
| static int32 nfunc; |
| |
| static byte **fname; |
| static int32 nfname; |
| |
| static void |
| dofunc(Sym *sym) |
| { |
| Func *f; |
| |
| switch(sym->symtype) { |
| case 't': |
| case 'T': |
| if(strcmp(sym->name, (byte*)"etext") == 0) |
| break; |
| if(func == nil) { |
| nfunc++; |
| break; |
| } |
| f = &func[nfunc++]; |
| f->name = gostring(sym->name); |
| f->entry = sym->value; |
| 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; |
| } |
| break; |
| case 'f': |
| if(fname == nil) { |
| if(sym->value >= nfname) |
| nfname = sym->value+1; |
| break; |
| } |
| fname[sym->value] = sym->name; |
| break; |
| } |
| } |
| |
| // put together the path name for a z entry. |
| // the f entries have been accumulated into fname already. |
| static void |
| makepath(byte *buf, int32 nbuf, byte *path) |
| { |
| int32 n, len; |
| byte *p, *ep, *q; |
| |
| if(nbuf <= 0) |
| return; |
| |
| 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 = findnull(q); |
| if(p+1+len >= ep) |
| break; |
| if(p > buf && p[-1] != '/') |
| *p++ = '/'; |
| mcpy(p, q, len+1); |
| p += len; |
| } |
| } |
| |
| // 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). |
| static void |
| dosrcline(Sym *sym) |
| { |
| static byte srcbuf[1000]; |
| static String srcstring; |
| static int32 lno, incstart; |
| static int32 nf, nhist; |
| Func *f; |
| |
| switch(sym->symtype) { |
| case 't': |
| case 'T': |
| if(strcmp(sym->name, (byte*)"etext") == 0) |
| break; |
| f = &func[nf++]; |
| f->src = srcstring; |
| f->ln0 += lno; |
| break; |
| case 'z': |
| if(sym->value == 1) { |
| // entry for main source file for a new object. |
| makepath(srcbuf, sizeof srcbuf, sym->name+1); |
| srcstring = gostring(srcbuf); |
| lno = 0; |
| nhist = 0; |
| } else { |
| // push or pop of included file. |
| makepath(srcbuf, sizeof srcbuf, sym->name+1); |
| if(srcbuf[0] != '\0') { |
| if(nhist++ == 0) |
| incstart = sym->value; |
| }else{ |
| if(--nhist == 0) |
| lno -= sym->value - incstart; |
| } |
| } |
| } |
| } |
| |
| enum { PcQuant = 1 }; |
| |
| // Interpret pc/ln table, saving the subpiece for each func. |
| static void |
| splitpcln(void) |
| { |
| int32 line; |
| uintptr pc; |
| byte *p, *ep; |
| Func *f, *ef; |
| int32 *v; |
| |
| // pc/ln table bounds |
| v = SYMCOUNTS; |
| p = SYMDATA; |
| p += v[0]; |
| ep = p+v[1]; |
| |
| f = func; |
| ef = func + nfunc; |
| pc = func[0].entry; // text base |
| f->pcln.array = p; |
| f->pc0 = pc - PcQuant; |
| line = 0; |
| for(; p < ep; p++) { |
| if(f < ef && pc >= (f+1)->entry) { |
| f->pcln.nel = p - f->pcln.array; |
| f->pcln.cap = f->pcln.nel; |
| f++; |
| f->pcln.array = p; |
| f->pc0 = pc; |
| f->ln0 = line; |
| } |
| if(*p == 0) { |
| // 4 byte add to line |
| line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; |
| p += 4; |
| } else if(*p <= 64) { |
| line += *p; |
| } else if(*p <= 128) { |
| line -= *p - 64; |
| } else { |
| pc += PcQuant*(*p - 129); |
| } |
| pc += PcQuant; |
| } |
| if(f < ef) { |
| f->pcln.nel = p - f->pcln.array; |
| f->pcln.cap = f->pcln.nel; |
| } |
| } |
| |
| |
| // Return actual file line number for targetpc in func f. |
| // (Source file is f->src.) |
| int32 |
| funcline(Func *f, uint64 targetpc) |
| { |
| byte *p, *ep; |
| uintptr pc; |
| int32 line; |
| |
| p = f->pcln.array; |
| ep = p + f->pcln.nel; |
| pc = f->pc0; |
| line = f->ln0; |
| for(; p < ep; p++) { |
| if(pc >= targetpc) |
| return line; |
| if(*p == 0) { |
| line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4]; |
| p += 4; |
| } else if(*p <= 64) { |
| line += *p; |
| } else if(*p <= 128) { |
| line -= *p - 64; |
| } else { |
| pc += PcQuant*(*p - 129); |
| } |
| pc += PcQuant; |
| } |
| return line; |
| } |
| |
| static void |
| buildfuncs(void) |
| { |
| extern byte etext[]; |
| |
| if(func != nil) |
| return; |
| // count funcs, fnames |
| nfunc = 0; |
| nfname = 0; |
| walksymtab(dofunc); |
| |
| // initialize tables |
| func = mal((nfunc+1)*sizeof func[0]); |
| func[nfunc].entry = (uint64)etext; |
| fname = mal(nfname*sizeof fname[0]); |
| nfunc = 0; |
| walksymtab(dofunc); |
| |
| // split pc/ln table by func |
| splitpcln(); |
| |
| // record src file and line info for each func |
| walksymtab(dosrcline); |
| } |
| |
| Func* |
| findfunc(uintptr addr) |
| { |
| Func *f; |
| int32 nf, n; |
| |
| if(func == nil) |
| buildfuncs(); |
| if(nfunc == 0) |
| return nil; |
| if(addr < func[0].entry || addr >= func[nfunc].entry) |
| return nil; |
| |
| // binary search to find func with entry <= addr. |
| f = func; |
| nf = nfunc; |
| while(nf > 0) { |
| n = nf/2; |
| if(f[n].entry <= addr && addr < f[n+1].entry) |
| return &f[n]; |
| else if(addr < f[n].entry) |
| nf = n; |
| else { |
| f += n+1; |
| nf -= n+1; |
| } |
| } |
| |
| // can't get here -- we already checked above |
| // that the address was in the table bounds. |
| // this can only happen if the table isn't sorted |
| // by address or if the binary search above is buggy. |
| prints("findfunc unreachable\n"); |
| return nil; |
| } |