diff options
Diffstat (limited to 'src/cmd/ld/pcln.c')
-rw-r--r-- | src/cmd/ld/pcln.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/cmd/ld/pcln.c b/src/cmd/ld/pcln.c new file mode 100644 index 000000000..4c2ffa78e --- /dev/null +++ b/src/cmd/ld/pcln.c @@ -0,0 +1,244 @@ +// 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; 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, "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); +} |