summaryrefslogtreecommitdiff
path: root/src/cmd/ld/pcln.c
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2014-06-19 09:22:53 +0200
committerMichael Stapelberg <stapelberg@debian.org>2014-06-19 09:22:53 +0200
commit8a39ee361feb9bf46d728ff1ba4f07ca1d9610b1 (patch)
tree4449f2036cccf162e8417cc5841a35815b3e7ac5 /src/cmd/ld/pcln.c
parentc8bf49ef8a92e2337b69c14b9b88396efe498600 (diff)
downloadgolang-8a39ee361feb9bf46d728ff1ba4f07ca1d9610b1.tar.gz
Imported Upstream version 1.3upstream/1.3
Diffstat (limited to 'src/cmd/ld/pcln.c')
-rw-r--r--src/cmd/ld/pcln.c244
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);
+}