| // 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 "../../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; i<nfiles; i++) { |
| f = files[i]; |
| if(f->type != 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, "runtime.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; i<pcln->npcdata; 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; i<pcln->nfuncdata; 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); |
| } |