github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/liblink/pcln.c (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 #include <u.h> 6 #include <libc.h> 7 #include <bio.h> 8 #include <link.h> 9 10 static void 11 addvarint(Link *ctxt, Pcdata *d, uint32 val) 12 { 13 int32 n; 14 uint32 v; 15 uchar *p; 16 17 USED(ctxt); 18 19 n = 0; 20 for(v = val; v >= 0x80; v >>= 7) 21 n++; 22 n++; 23 24 if(d->n + n > d->m) { 25 d->m = (d->n + n)*2; 26 d->p = erealloc(d->p, d->m); 27 } 28 29 p = d->p + d->n; 30 for(v = val; v >= 0x80; v >>= 7) 31 *p++ = v | 0x80; 32 *p = v; 33 d->n += n; 34 } 35 36 // funcpctab writes to dst a pc-value table mapping the code in func to the values 37 // returned by valfunc parameterized by arg. The invocation of valfunc to update the 38 // current value is, for each p, 39 // 40 // val = valfunc(func, val, p, 0, arg); 41 // record val as value at p->pc; 42 // val = valfunc(func, val, p, 1, arg); 43 // 44 // where func is the function, val is the current value, p is the instruction being 45 // considered, and arg can be used to further parameterize valfunc. 46 static void 47 funcpctab(Link *ctxt, Pcdata *dst, LSym *func, char *desc, int32 (*valfunc)(Link*, LSym*, int32, Prog*, int32, void*), void* arg) 48 { 49 int dbg, i; 50 int32 oldval, val, started; 51 uint32 delta; 52 vlong pc; 53 Prog *p; 54 55 // To debug a specific function, uncomment second line and change name. 56 dbg = 0; 57 //dbg = strcmp(func->name, "main.main") == 0; 58 //dbg = strcmp(desc, "pctofile") == 0; 59 60 ctxt->debugpcln += dbg; 61 62 dst->n = 0; 63 64 if(ctxt->debugpcln) 65 Bprint(ctxt->bso, "funcpctab %s [valfunc=%s]\n", func->name, desc); 66 67 val = -1; 68 oldval = val; 69 if(func->text == nil) { 70 ctxt->debugpcln -= dbg; 71 return; 72 } 73 74 pc = func->text->pc; 75 76 if(ctxt->debugpcln) 77 Bprint(ctxt->bso, "%6llux %6d %P\n", pc, val, func->text); 78 79 started = 0; 80 for(p=func->text; p != nil; p = p->link) { 81 // Update val. If it's not changing, keep going. 82 val = valfunc(ctxt, func, val, p, 0, arg); 83 if(val == oldval && started) { 84 val = valfunc(ctxt, func, val, p, 1, arg); 85 if(ctxt->debugpcln) 86 Bprint(ctxt->bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); 87 continue; 88 } 89 90 // If the pc of the next instruction is the same as the 91 // pc of this instruction, this instruction is not a real 92 // instruction. Keep going, so that we only emit a delta 93 // for a true instruction boundary in the program. 94 if(p->link && p->link->pc == p->pc) { 95 val = valfunc(ctxt, func, val, p, 1, arg); 96 if(ctxt->debugpcln) 97 Bprint(ctxt->bso, "%6llux %6s %P\n", (vlong)p->pc, "", p); 98 continue; 99 } 100 101 // The table is a sequence of (value, pc) pairs, where each 102 // pair states that the given value is in effect from the current position 103 // up to the given pc, which becomes the new current position. 104 // To generate the table as we scan over the program instructions, 105 // we emit a "(value" when pc == func->value, and then 106 // each time we observe a change in value we emit ", pc) (value". 107 // When the scan is over, we emit the closing ", pc)". 108 // 109 // The table is delta-encoded. The value deltas are signed and 110 // transmitted in zig-zag form, where a complement bit is placed in bit 0, 111 // and the pc deltas are unsigned. Both kinds of deltas are sent 112 // as variable-length little-endian base-128 integers, 113 // where the 0x80 bit indicates that the integer continues. 114 115 if(ctxt->debugpcln) 116 Bprint(ctxt->bso, "%6llux %6d %P\n", (vlong)p->pc, val, p); 117 118 if(started) { 119 addvarint(ctxt, dst, (p->pc - pc) / ctxt->arch->minlc); 120 pc = p->pc; 121 } 122 delta = val - oldval; 123 if(delta>>31) 124 delta = 1 | ~(delta<<1); 125 else 126 delta <<= 1; 127 addvarint(ctxt, dst, delta); 128 oldval = val; 129 started = 1; 130 val = valfunc(ctxt, func, val, p, 1, arg); 131 } 132 133 if(started) { 134 if(ctxt->debugpcln) 135 Bprint(ctxt->bso, "%6llux done\n", (vlong)func->text->pc+func->size); 136 addvarint(ctxt, dst, (func->value+func->size - pc) / ctxt->arch->minlc); 137 addvarint(ctxt, dst, 0); // terminator 138 } 139 140 if(ctxt->debugpcln) { 141 Bprint(ctxt->bso, "wrote %d bytes to %p\n", dst->n, dst); 142 for(i=0; i<dst->n; i++) 143 Bprint(ctxt->bso, " %02ux", dst->p[i]); 144 Bprint(ctxt->bso, "\n"); 145 } 146 147 ctxt->debugpcln -= dbg; 148 } 149 150 // pctofileline computes either the file number (arg == 0) 151 // or the line number (arg == 1) to use at p. 152 // Because p->lineno applies to p, phase == 0 (before p) 153 // takes care of the update. 154 static int32 155 pctofileline(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) 156 { 157 int32 i, l; 158 LSym *f; 159 Pcln *pcln; 160 161 USED(sym); 162 163 if(p->as == ctxt->arch->ATEXT || p->as == ctxt->arch->ANOP || p->as == ctxt->arch->AUSEFIELD || p->lineno == 0 || phase == 1) 164 return oldval; 165 linkgetline(ctxt, p->lineno, &f, &l); 166 if(f == nil) { 167 // print("getline failed for %s %P\n", ctxt->cursym->name, p); 168 return oldval; 169 } 170 if(arg == nil) 171 return l; 172 pcln = arg; 173 174 if(f == pcln->lastfile) 175 return pcln->lastindex; 176 177 for(i=0; i<pcln->nfile; i++) { 178 if(pcln->file[i] == f) { 179 pcln->lastfile = f; 180 pcln->lastindex = i; 181 return i; 182 } 183 } 184 185 if(pcln->nfile >= pcln->mfile) { 186 pcln->mfile = (pcln->nfile+1)*2; 187 pcln->file = erealloc(pcln->file, pcln->mfile*sizeof pcln->file[0]); 188 } 189 pcln->file[pcln->nfile++] = f; 190 pcln->lastfile = f; 191 pcln->lastindex = i; 192 return i; 193 } 194 195 // pctospadj computes the sp adjustment in effect. 196 // It is oldval plus any adjustment made by p itself. 197 // The adjustment by p takes effect only after p, so we 198 // apply the change during phase == 1. 199 static int32 200 pctospadj(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) 201 { 202 USED(arg); 203 USED(sym); 204 205 if(oldval == -1) // starting 206 oldval = 0; 207 if(phase == 0) 208 return oldval; 209 if(oldval + p->spadj < -10000 || oldval + p->spadj > 1100000000) { 210 ctxt->diag("overflow in spadj: %d + %d = %d", oldval, p->spadj, oldval + p->spadj); 211 sysfatal("bad code"); 212 } 213 return oldval + p->spadj; 214 } 215 216 // pctopcdata computes the pcdata value in effect at p. 217 // A PCDATA instruction sets the value in effect at future 218 // non-PCDATA instructions. 219 // Since PCDATA instructions have no width in the final code, 220 // it does not matter which phase we use for the update. 221 static int32 222 pctopcdata(Link *ctxt, LSym *sym, int32 oldval, Prog *p, int32 phase, void *arg) 223 { 224 USED(sym); 225 226 if(phase == 0 || p->as != ctxt->arch->APCDATA || p->from.offset != (uintptr)arg) 227 return oldval; 228 if((int32)p->to.offset != p->to.offset) { 229 ctxt->diag("overflow in PCDATA instruction: %P", p); 230 sysfatal("bad code"); 231 } 232 return p->to.offset; 233 } 234 235 void 236 linkpcln(Link *ctxt, LSym *cursym) 237 { 238 Prog *p; 239 Pcln *pcln; 240 int i, npcdata, nfuncdata, n; 241 uint32 *havepc, *havefunc; 242 243 ctxt->cursym = cursym; 244 245 pcln = emallocz(sizeof *pcln); 246 cursym->pcln = pcln; 247 248 npcdata = 0; 249 nfuncdata = 0; 250 for(p = cursym->text; p != nil; p = p->link) { 251 if(p->as == ctxt->arch->APCDATA && p->from.offset >= npcdata) 252 npcdata = p->from.offset+1; 253 if(p->as == ctxt->arch->AFUNCDATA && p->from.offset >= nfuncdata) 254 nfuncdata = p->from.offset+1; 255 } 256 257 pcln->pcdata = emallocz(npcdata*sizeof pcln->pcdata[0]); 258 pcln->npcdata = npcdata; 259 pcln->funcdata = emallocz(nfuncdata*sizeof pcln->funcdata[0]); 260 pcln->funcdataoff = emallocz(nfuncdata*sizeof pcln->funcdataoff[0]); 261 pcln->nfuncdata = nfuncdata; 262 263 funcpctab(ctxt, &pcln->pcsp, cursym, "pctospadj", pctospadj, nil); 264 funcpctab(ctxt, &pcln->pcfile, cursym, "pctofile", pctofileline, pcln); 265 funcpctab(ctxt, &pcln->pcline, cursym, "pctoline", pctofileline, nil); 266 267 // tabulate which pc and func data we have. 268 n = ((npcdata+31)/32 + (nfuncdata+31)/32)*4; 269 havepc = emallocz(n); 270 havefunc = havepc + (npcdata+31)/32; 271 for(p = cursym->text; p != nil; p = p->link) { 272 if(p->as == ctxt->arch->AFUNCDATA) { 273 if((havefunc[p->from.offset/32]>>(p->from.offset%32))&1) 274 ctxt->diag("multiple definitions for FUNCDATA $%d", p->from.offset); 275 havefunc[p->from.offset/32] |= 1<<(p->from.offset%32); 276 } 277 if(p->as == ctxt->arch->APCDATA) 278 havepc[p->from.offset/32] |= 1<<(p->from.offset%32); 279 } 280 // pcdata. 281 for(i=0; i<npcdata; i++) { 282 if(((havepc[i/32]>>(i%32))&1) == 0) 283 continue; 284 funcpctab(ctxt, &pcln->pcdata[i], cursym, "pctopcdata", pctopcdata, (void*)(uintptr)i); 285 } 286 free(havepc); 287 288 // funcdata 289 if(nfuncdata > 0) { 290 for(p = cursym->text; p != nil; p = p->link) { 291 if(p->as == ctxt->arch->AFUNCDATA) { 292 i = p->from.offset; 293 pcln->funcdataoff[i] = p->to.offset; 294 if(p->to.type != ctxt->arch->D_CONST) { 295 // TODO: Dedup. 296 //funcdata_bytes += p->to.sym->size; 297 pcln->funcdata[i] = p->to.sym; 298 } 299 } 300 } 301 } 302 } 303 304 // iteration over encoded pcdata tables. 305 306 static uint32 307 getvarint(uchar **pp) 308 { 309 uchar *p; 310 int shift; 311 uint32 v; 312 313 v = 0; 314 p = *pp; 315 for(shift = 0;; shift += 7) { 316 v |= (uint32)(*p & 0x7F) << shift; 317 if(!(*p++ & 0x80)) 318 break; 319 } 320 *pp = p; 321 return v; 322 } 323 324 void 325 pciternext(Pciter *it) 326 { 327 uint32 v; 328 int32 dv; 329 330 it->pc = it->nextpc; 331 if(it->done) 332 return; 333 if(it->p >= it->d.p + it->d.n) { 334 it->done = 1; 335 return; 336 } 337 338 // value delta 339 v = getvarint(&it->p); 340 if(v == 0 && !it->start) { 341 it->done = 1; 342 return; 343 } 344 it->start = 0; 345 dv = (int32)(v>>1) ^ ((int32)(v<<31)>>31); 346 it->value += dv; 347 348 // pc delta 349 v = getvarint(&it->p); 350 it->nextpc = it->pc + v*it->pcscale; 351 } 352 353 void 354 pciterinit(Link *ctxt, Pciter *it, Pcdata *d) 355 { 356 it->d = *d; 357 it->p = it->d.p; 358 it->pc = 0; 359 it->nextpc = 0; 360 it->value = -1; 361 it->start = 1; 362 it->done = 0; 363 it->pcscale = ctxt->arch->minlc; 364 pciternext(it); 365 }