// Copyright 2013 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. #include "l.h" #include "lib.h" #include "../../pkg/runtime/funcdata.h" static void addvarint(Pcdata *d, uint32 val) { int32 n; uint32 v; uchar *p; n = 0; for(v = val; v >= 0x80; v >>= 7) n++; n++; if(d->n + n > d->m) { d->m = (d->n + n)*2; d->p = erealloc(d->p, d->m); } p = d->p + d->n; for(v = val; v >= 0x80; v >>= 7) *p++ = v | 0x80; *p = v; d->n += n; } static int32 addpctab(LSym *ftab, int32 off, Pcdata *d) { int32 start; start = ftab->np; symgrow(ctxt, ftab, start + d->n); memmove(ftab->p + start, d->p, d->n); return setuint32(ctxt, ftab, off, start); } static int32 ftabaddstring(LSym *ftab, char *s) { int32 n, start; n = strlen(s)+1; start = ftab->np; symgrow(ctxt, ftab, start+n+1); strcpy((char*)ftab->p + start, s); return start; } static void renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d) { int i; LSym *f; Pcdata out; Pciter it; uint32 v; int32 oldval, newval, val, dv; // Give files numbers. for(i=0; itype != SFILEPATH) { f->value = ++ctxt->nhistfile; f->type = SFILEPATH; f->next = ctxt->filesyms; ctxt->filesyms = f; } } newval = -1; memset(&out, 0, sizeof out); for(pciterinit(ctxt, &it, d); !it.done; pciternext(&it)) { // value delta oldval = it.value; if(oldval == -1) val = -1; else { if(oldval < 0 || oldval >= nfiles) sysfatal("bad pcdata %d", oldval); val = files[oldval]->value; } dv = val - newval; newval = val; v = (uint32)(dv<<1) ^ (uint32)(int32)(dv>>31); addvarint(&out, v); // pc delta addvarint(&out, (it.nextpc - it.pc) / it.pcscale); } // terminating value delta addvarint(&out, 0); free(d->p); *d = out; } // pclntab initializes the pclntab symbol with // runtime function and file name information. void pclntab(void) { int32 i, nfunc, start, funcstart; LSym *ftab, *s; int32 off, end, frameptrsize; int64 funcdata_bytes; Pcln *pcln; Pciter it; static Pcln zpcln; funcdata_bytes = 0; ftab = linklookup(ctxt, "pclntab", 0); ftab->type = SPCLNTAB; ftab->reachable = 1; // See golang.org/s/go12symtab for the format. Briefly: // 8-byte header // nfunc [PtrSize bytes] // function table, alternating PC and offset to func struct [each entry PtrSize bytes] // end PC [PtrSize bytes] // offset to file table [4 bytes] nfunc = 0; for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) nfunc++; symgrow(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4); setuint32(ctxt, ftab, 0, 0xfffffffb); setuint8(ctxt, ftab, 6, MINLC); setuint8(ctxt, ftab, 7, PtrSize); setuintxx(ctxt, ftab, 8, nfunc, PtrSize); nfunc = 0; for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next, nfunc++) { pcln = ctxt->cursym->pcln; if(pcln == nil) pcln = &zpcln; funcstart = ftab->np; funcstart += -ftab->np & (PtrSize-1); setaddr(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize, ctxt->cursym); setuintxx(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize); // fixed size of struct, checked below off = funcstart; end = funcstart + PtrSize + 3*4 + 5*4 + pcln->npcdata*4 + pcln->nfuncdata*PtrSize; if(pcln->nfuncdata > 0 && (end&(PtrSize-1))) end += 4; symgrow(ctxt, ftab, end); // entry uintptr off = setaddr(ctxt, ftab, off, ctxt->cursym); // name int32 off = setuint32(ctxt, ftab, off, ftabaddstring(ftab, ctxt->cursym->name)); // args int32 // TODO: Move into funcinfo. off = setuint32(ctxt, ftab, off, ctxt->cursym->args); // 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. frameptrsize = PtrSize; if(ctxt->cursym->leaf) frameptrsize = 0; off = setuint32(ctxt, ftab, off, ctxt->cursym->locals + frameptrsize); if(pcln != &zpcln) { renumberfiles(ctxt, pcln->file, pcln->nfile, &pcln->pcfile); if(0) { // Sanity check the new numbering for(pciterinit(ctxt, &it, &pcln->pcfile); !it.done; pciternext(&it)) { if(it.value < 1 || it.value > ctxt->nhistfile) { diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, ctxt->nhistfile); errorexit(); } } } } // pcdata off = addpctab(ftab, off, &pcln->pcsp); off = addpctab(ftab, off, &pcln->pcfile); off = addpctab(ftab, off, &pcln->pcline); off = setuint32(ctxt, ftab, off, pcln->npcdata); off = setuint32(ctxt, ftab, off, pcln->nfuncdata); for(i=0; inpcdata; i++) off = addpctab(ftab, off, &pcln->pcdata[i]); // funcdata, must be pointer-aligned and we're only int32-aligned. // Missing funcdata will be 0 (nil pointer). if(pcln->nfuncdata > 0) { if(off&(PtrSize-1)) off += 4; for(i=0; infuncdata; i++) { if(pcln->funcdata[i] == nil) setuintxx(ctxt, ftab, off+PtrSize*i, pcln->funcdataoff[i], PtrSize); else { // TODO: Dedup. funcdata_bytes += pcln->funcdata[i]->size; setaddrplus(ctxt, ftab, off+PtrSize*i, pcln->funcdata[i], pcln->funcdataoff[i]); } } off += pcln->nfuncdata*PtrSize; } if(off != end) { diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln->npcdata, pcln->nfuncdata, PtrSize); errorexit(); } // Final entry of table is just end pc. if(ctxt->cursym->next == nil) setaddrplus(ctxt, ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, ctxt->cursym, ctxt->cursym->size); } // Start file table. start = ftab->np; start += -ftab->np & (PtrSize-1); setuint32(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start); symgrow(ctxt, ftab, start+(ctxt->nhistfile+1)*4); setuint32(ctxt, ftab, start, ctxt->nhistfile); for(s = ctxt->filesyms; s != S; s = s->next) setuint32(ctxt, ftab, start + s->value*4, ftabaddstring(ftab, s->name)); ftab->size = ftab->np; if(debug['v']) Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes); }