| // 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 <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <link.h> |
| |
| static void |
| addvarint(Link *ctxt, Pcdata *d, uint32 val) |
| { |
| int32 n; |
| uint32 v; |
| uchar *p; |
| |
| USED(ctxt); |
| |
| 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; |
| } |
| |
| // funcpctab writes to dst a pc-value table mapping the code in func to the values |
| // returned by valfunc parameterized by arg. The invocation of valfunc to update the |
| // current value is, for each p, |
| // |
| // val = valfunc(func, val, p, 0, arg); |
| // record val as value at p->pc; |
| // val = valfunc(func, val, p, 1, arg); |
| // |
| // where func is the function, val is the current value, p is the instruction being |
| // considered, and arg can be used to further parameterize valfunc. |
| static void |
| funcpctab(Link *ctxt, Pcdata *dst, LSym *func, char *desc, int32 (*valfunc)(Link*, LSym*, int32, Prog*, int32, void*), void* arg) |
| { |
| int dbg, i; |
| int32 oldval, val, started; |
| uint32 delta; |
| vlong pc; |
| Prog *p; |
| |
| // To debug a specific function, uncomment second line and change name. |
| dbg = 0; |
| //dbg = strcmp(func->name, "main.main") == 0; |
| //dbg = strcmp(desc, "pctofile") == 0; |
| |
| ctxt->debugpcln += dbg; |
| |
| dst->n = 0; |
| |
| if(ctxt->debugpcln) |
| Bprint(ctxt->bso, "funcpctab %s [valfunc=%s]\n", func->name, desc); |
| |
| val = -1; |
| oldval = val; |
| if(func->text == nil) { |
| ctxt->debugpcln -= dbg; |
| return; |
| } |
| |
| pc = func->text->pc; |
| |
| if(ctxt->debugpcln) |
| Bprint(ctxt->bso, "%6llux %6d %P\n", pc, val, func->text); |
| |
| started = 0; |
| for(p=func->text; p != nil; p = p->link) { |
| // Update val. If it's not changing, keep going. |
| val = valfunc(ctxt, func, val, p, 0, arg); |
| if(val == oldval && started) { |
| val = valfunc(ctxt, func, val, p, 1, arg); |
| if(ctxt->debugpcln) |
| Bprint(ctxt->bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); |
| continue; |
| } |
| |
| // If the pc of the next instruction is the same as the |
| // pc of this instruction, this instruction is not a real |
| // instruction. Keep going, so that we only emit a delta |
| // for a true instruction boundary in the program. |
| if(p->link && p->link->pc == p->pc) { |
| val = valfunc(ctxt, func, val, p, 1, arg); |
| if(ctxt->debugpcln) |
| Bprint(ctxt->bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); |
| continue; |
| } |
| |
| // The table is a sequence of (value, pc) pairs, where each |
| // pair states that the given value is in effect from the current position |
| // up to the given pc, which becomes the new current position. |
| // To generate the table as we scan over the program instructions, |
| // we emit a "(value" when pc == func->value, and then |
| // each time we observe a change in value we emit ", pc) (value". |
| // When the scan is over, we emit the closing ", pc)". |
| // |
| // The table is delta-encoded. The value deltas are signed and |
| // transmitted in zig-zag form, where a complement bit is placed in bit 0, |
| // and the pc deltas are unsigned. Both kinds of deltas are sent |
| // as variable-length little-endian base-128 integers, |
| // where the 0x80 bit indicates that the integer continues. |
| |
| if(ctxt->debugpcln) |
| Bprint(ctxt->bso, "%6llux %6d %P\n", (vlong)p->pc, val, p); |
| |
| if(started) { |
| addvarint(ctxt, dst, (p->pc - pc) / ctxt->arch->minlc); |
| pc = p->pc; |
| } |
| delta = val - oldval; |
| if(delta>>31) |
| delta = 1 | ~(delta<<1); |
| else |
| delta <<= 1; |
| addvarint(ctxt, dst, delta); |
| oldval = val; |
| started = 1; |
| val = valfunc(ctxt, func, val, p, 1, arg); |
| } |
| |
| if(started) { |
| if(ctxt->debugpcln) |
| Bprint(ctxt->bso, "%6llux done\n", (vlong)func->text->pc+func->size); |
| addvarint(ctxt, dst, (func->value+func->size - pc) / ctxt->arch->minlc); |
| addvarint(ctxt, dst, 0); // terminator |
| } |
| |
| if(ctxt->debugpcln) { |
| Bprint(ctxt->bso, "wrote %d bytes to %p\n", dst->n, dst); |
| for(i=0; i<dst->n; i++) |
| Bprint(ctxt->bso, " %02ux", dst->p[i]); |
| Bprint(ctxt->bso, "\n"); |
| } |
| |
| ctxt->debugpcln -= dbg; |
| } |
| |
| // pctofileline computes either the file number (arg == 0) |
| // or the line number (arg == 1) to use at p. |
| // Because p->lineno applies to p, phase == 0 (before p) |
| // takes care of the update. |
| static int32 |
| pctofileline(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) |
| { |
| int32 i, l; |
| LSym *f; |
| Pcln *pcln; |
| |
| USED(sym); |
| |
| if(p->as == ctxt->arch->ATEXT || p->as == ctxt->arch->ANOP || p->as == ctxt->arch->AUSEFIELD || p->lineno == 0 || phase == 1) |
| return oldval; |
| linkgetline(ctxt, p->lineno, &f, &l); |
| if(f == nil) { |
| // print("getline failed for %s %P\n", ctxt->cursym->name, p); |
| return oldval; |
| } |
| if(arg == nil) |
| return l; |
| pcln = arg; |
| |
| if(f == pcln->lastfile) |
| return pcln->lastindex; |
| |
| for(i=0; i<pcln->nfile; i++) { |
| if(pcln->file[i] == f) { |
| pcln->lastfile = f; |
| pcln->lastindex = i; |
| return i; |
| } |
| } |
| |
| if(pcln->nfile >= pcln->mfile) { |
| pcln->mfile = (pcln->nfile+1)*2; |
| pcln->file = erealloc(pcln->file, pcln->mfile*sizeof pcln->file[0]); |
| } |
| pcln->file[pcln->nfile++] = f; |
| pcln->lastfile = f; |
| pcln->lastindex = i; |
| return i; |
| } |
| |
| // pctospadj computes the sp adjustment in effect. |
| // It is oldval plus any adjustment made by p itself. |
| // The adjustment by p takes effect only after p, so we |
| // apply the change during phase == 1. |
| static int32 |
| pctospadj(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) |
| { |
| USED(arg); |
| USED(sym); |
| |
| if(oldval == -1) // starting |
| oldval = 0; |
| if(phase == 0) |
| return oldval; |
| if(oldval + p->spadj < -10000 || oldval + p->spadj > 1100000000) { |
| ctxt->diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj); |
| sysfatal("bad code"); |
| } |
| return oldval + p->spadj; |
| } |
| |
| // pctopcdata computes the pcdata value in effect at p. |
| // A PCDATA instruction sets the value in effect at future |
| // non-PCDATA instructions. |
| // Since PCDATA instructions have no width in the final code, |
| // it does not matter which phase we use for the update. |
| static int32 |
| pctopcdata(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) |
| { |
| USED(sym); |
| |
| if(phase == 0 || p->as != ctxt->arch->APCDATA || p->from.offset != (uintptr)arg) |
| return oldval; |
| if((int32)p->to.offset != p->to.offset) { |
| ctxt->diag("overflow in PCDATA instruction: %P", p); |
| sysfatal("bad code"); |
| } |
| return p->to.offset; |
| } |
| |
| void |
| linkpcln(Link *ctxt, LSym *cursym) |
| { |
| Prog *p; |
| Pcln *pcln; |
| int i, npcdata, nfuncdata, n; |
| uint32 *havepc, *havefunc; |
| |
| ctxt->cursym = cursym; |
| |
| pcln = emallocz(sizeof *pcln); |
| cursym->pcln = pcln; |
| |
| npcdata = 0; |
| nfuncdata = 0; |
| for(p = cursym->text; p != nil; p = p->link) { |
| if(p->as == ctxt->arch->APCDATA && p->from.offset >= npcdata) |
| npcdata = p->from.offset+1; |
| if(p->as == ctxt->arch->AFUNCDATA && p->from.offset >= nfuncdata) |
| nfuncdata = p->from.offset+1; |
| } |
| |
| pcln->pcdata = emallocz(npcdata*sizeof pcln->pcdata[0]); |
| pcln->npcdata = npcdata; |
| pcln->funcdata = emallocz(nfuncdata*sizeof pcln->funcdata[0]); |
| pcln->funcdataoff = emallocz(nfuncdata*sizeof pcln->funcdataoff[0]); |
| pcln->nfuncdata = nfuncdata; |
| |
| funcpctab(ctxt, &pcln->pcsp, cursym, "pctospadj", pctospadj, nil); |
| funcpctab(ctxt, &pcln->pcfile, cursym, "pctofile", pctofileline, pcln); |
| funcpctab(ctxt, &pcln->pcline, cursym, "pctoline", pctofileline, nil); |
| |
| // tabulate which pc and func data we have. |
| n = ((npcdata+31)/32 + (nfuncdata+31)/32)*4; |
| havepc = emallocz(n); |
| havefunc = havepc + (npcdata+31)/32; |
| for(p = cursym->text; p != nil; p = p->link) { |
| if(p->as == ctxt->arch->AFUNCDATA) { |
| if((havefunc[p->from.offset/32]>>(p->from.offset%32))&1) |
| ctxt->diag("multiple definitions for FUNCDATA $%d", p->from.offset); |
| havefunc[p->from.offset/32] |= 1<<(p->from.offset%32); |
| } |
| if(p->as == ctxt->arch->APCDATA) |
| havepc[p->from.offset/32] |= 1<<(p->from.offset%32); |
| } |
| // pcdata. |
| for(i=0; i<npcdata; i++) { |
| if(((havepc[i/32]>>(i%32))&1) == 0) |
| continue; |
| funcpctab(ctxt, &pcln->pcdata[i], cursym, "pctopcdata", pctopcdata, (void*)(uintptr)i); |
| } |
| free(havepc); |
| |
| // funcdata |
| if(nfuncdata > 0) { |
| for(p = cursym->text; p != nil; p = p->link) { |
| if(p->as == ctxt->arch->AFUNCDATA) { |
| i = p->from.offset; |
| pcln->funcdataoff[i] = p->to.offset; |
| if(p->to.type != ctxt->arch->D_CONST) { |
| // TODO: Dedup. |
| //funcdata_bytes += p->to.sym->size; |
| pcln->funcdata[i] = p->to.sym; |
| } |
| } |
| } |
| } |
| } |
| |
| // iteration over encoded pcdata tables. |
| |
| static uint32 |
| getvarint(uchar **pp) |
| { |
| uchar *p; |
| int shift; |
| uint32 v; |
| |
| v = 0; |
| p = *pp; |
| for(shift = 0;; shift += 7) { |
| v |= (uint32)(*p & 0x7F) << shift; |
| if(!(*p++ & 0x80)) |
| break; |
| } |
| *pp = p; |
| return v; |
| } |
| |
| void |
| pciternext(Pciter *it) |
| { |
| uint32 v; |
| int32 dv; |
| |
| it->pc = it->nextpc; |
| if(it->done) |
| return; |
| if(it->p >= it->d.p + it->d.n) { |
| it->done = 1; |
| return; |
| } |
| |
| // value delta |
| v = getvarint(&it->p); |
| if(v == 0 && !it->start) { |
| it->done = 1; |
| return; |
| } |
| it->start = 0; |
| dv = (int32)(v>>1) ^ ((int32)(v<<31)>>31); |
| it->value += dv; |
| |
| // pc delta |
| v = getvarint(&it->p); |
| it->nextpc = it->pc + v*it->pcscale; |
| } |
| |
| void |
| pciterinit(Link *ctxt, Pciter *it, Pcdata *d) |
| { |
| it->d = *d; |
| it->p = it->d.p; |
| it->pc = 0; |
| it->nextpc = 0; |
| it->value = -1; |
| it->start = 1; |
| it->done = 0; |
| it->pcscale = ctxt->arch->minlc; |
| pciternext(it); |
| } |