1 /* 2 * Dwarf call frame unwinding. 3 * 4 * The call frame unwinding values are encoded using a state machine 5 * like the pc<->line mapping, but it's a different machine. 6 * The expressions to generate the old values are similar in function to the 7 * ``dwarf expressions'' used for locations in the code, but of course not 8 * the same encoding. 9 */ 10 11 #include <precomp.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 #define trace 1 17 18 typedef struct State State; 19 struct State 20 { 21 ulong loc; 22 ulong endloc; 23 ulong iquantum; 24 ulong dquantum; 25 char *augmentation; 26 int version; 27 ulong rareg; 28 DwarfBuf init; 29 DwarfExpr *cfa; 30 DwarfExpr *ra; 31 DwarfExpr *r; 32 DwarfExpr *initr; 33 int nr; 34 DwarfExpr **stack; 35 int nstack; 36 }; 37 38 static int findfde(Dwarf*, ulong, State*, DwarfBuf*); 39 static int dexec(DwarfBuf*, State*, int); 40 41 int 42 dwarfunwind(Dwarf *d, ulong pc, DwarfExpr *cfa, DwarfExpr *ra, DwarfExpr *r, int nr) 43 { 44 int i, ret; 45 DwarfBuf fde, b; 46 DwarfExpr *initr; 47 State s; 48 49 initr = mallocz(nr*sizeof(initr[0]), 1); 50 if(initr == 0) 51 return -1; 52 53 memset(&s, 0, sizeof s); 54 s.loc = 0; 55 s.cfa = cfa; 56 s.ra = ra; 57 s.r = r; 58 s.nr = nr; 59 60 if(findfde(d, pc, &s, &fde) < 0){ 61 free(initr); 62 return -1; 63 } 64 65 memset(r, 0, nr*sizeof(r[0])); 66 for(i=0; i<nr; i++) 67 r[i].type = RuleSame; 68 if(trace) werrstr("s.init %p-%p, fde %p-%p", s.init.p, s.init.ep, fde.p, fde.ep); 69 b = s.init; 70 if(dexec(&b, &s, 0) < 0) 71 goto err; 72 73 s.initr = initr; 74 memmove(initr, r, nr*sizeof(initr[0])); 75 76 if(trace) werrstr("s.loc 0x%lx pc 0x%lx", s.loc, pc); 77 while(s.loc < pc){ 78 if(trace) werrstr("s.loc 0x%lx pc 0x%lx", s.loc, pc); 79 if(dexec(&fde, &s, 1) < 0) 80 goto err; 81 } 82 *ra = s.r[s.rareg]; 83 84 ret = 0; 85 goto out; 86 87 err: 88 ret = -1; 89 out: 90 free(initr); 91 for(i=0; i<s.nstack; i++) 92 free(s.stack[i]); 93 free(s.stack); 94 return ret; 95 } 96 97 /* 98 * XXX This turns out to be much more expensive than the actual 99 * running of the machine in dexec. It probably makes sense to 100 * cache the last 10 or so fde's we've found, since stack traces 101 * will keep asking for the same info over and over. 102 */ 103 static int 104 findfde(Dwarf *d, ulong pc, State *s, DwarfBuf *fde) 105 { 106 static int nbad; 107 char *aug; 108 uchar *next; 109 int i, vers; 110 ulong len, id, base, size; 111 DwarfBuf b; 112 113 if(d->frame.data == nil){ 114 werrstr("no frame debugging information"); 115 return -1; 116 } 117 b.d = d; 118 b.p = d->frame.data; 119 b.ep = b.p + d->frame.len; 120 b.addrsize = d->addrsize; 121 if(b.addrsize == 0) 122 b.addrsize = 4; /* where should i find this? */ 123 124 for(; b.p < b.ep; b.p = next){ 125 if((i = (b.p - d->frame.data) % b.addrsize)) 126 b.p += b.addrsize - i; 127 len = dwarfget4(&b); 128 if(len > b.ep-b.p){ 129 werrstr("bad length in cie/fde header"); 130 return -1; 131 } 132 next = b.p+len; 133 id = dwarfget4(&b); 134 if(id == 0xFFFFFFFF){ /* CIE */ 135 vers = dwarfget1(&b); 136 if (trace) werrstr("CIE len %x id %x vers %x", len, id, vers); 137 if(vers != 1 && vers != 2 && vers != 3){ 138 if(++nbad == 1) 139 werrstr("unknown cie version %d (wanted 1-3)", vers); 140 continue; 141 } 142 aug = dwarfgetstring(&b); 143 if(aug && *aug){ 144 if(++nbad == 1) 145 werrstr("unknown augmentation: %s", aug); 146 continue; 147 } 148 s->iquantum = dwarfget128(&b); 149 s->dquantum = dwarfget128s(&b); 150 s->rareg = dwarfget128(&b); 151 if(s->rareg > s->nr){ 152 werrstr("return address is register %d but only have %d registers", 153 s->rareg, s->nr); 154 return -1; 155 } 156 s->init.p = b.p; 157 s->init.ep = next; 158 }else{ /* FDE */ 159 base = dwarfgetaddr(&b); 160 size = dwarfgetaddr(&b); 161 if (trace) werrstr("FDE: base %x-%x (want pc %x)", base, base+size, pc); 162 fde->p = b.p; 163 fde->ep = next; 164 s->loc = base; 165 s->endloc = base+size; 166 if(base <= pc && pc < base+size) 167 return 0; 168 } 169 } 170 werrstr("cannot find call frame information for pc 0x%lx", pc); 171 return -1; 172 173 } 174 175 static int 176 checkreg(State *s, long r) 177 { 178 if(r < 0 || r >= s->nr){ 179 werrstr("bad register number 0x%lx", r); 180 return -1; 181 } 182 return 0; 183 } 184 185 static int 186 dexec(DwarfBuf *b, State *s, int locstop) 187 { 188 int c; 189 long arg1, arg2; 190 DwarfExpr *e; 191 192 for(;;){ 193 if(b->p == b->ep){ 194 if(s->initr) 195 s->loc = s->endloc; 196 werrstr("end dexec"); 197 return 0; 198 } 199 c = dwarfget1(b); 200 if(b->p == nil){ 201 werrstr("ran out of instructions during cfa program"); 202 if(trace) werrstr("%r"); 203 return -1; 204 } 205 if(trace) werrstr("+ loc=0x%x op 0x%x ", s->loc, c); 206 switch(c>>6){ 207 case 1: /* advance location */ 208 arg1 = c&0x3F; 209 advance: 210 if(trace) werrstr("loc += %ld", arg1*s->iquantum); 211 s->loc += arg1 * s->iquantum; 212 if(locstop) 213 return 0; 214 continue; 215 216 case 2: /* offset rule */ 217 arg1 = c&0x3F; 218 arg2 = dwarfget128(b); 219 offset: 220 if(trace) werrstr("r%ld += %ld", arg1, arg2*s->dquantum); 221 if(checkreg(s, arg1) < 0) 222 return -1; 223 s->r[arg1].type = RuleCfaOffset; 224 s->r[arg1].offset = arg2 * s->dquantum; 225 continue; 226 227 case 3: /* restore initial setting */ 228 arg1 = c&0x3F; 229 restore: 230 if(trace) werrstr("r%ld = init", arg1); 231 if(checkreg(s, arg1) < 0) 232 return -1; 233 s->r[arg1] = s->initr[arg1]; 234 continue; 235 } 236 237 switch(c){ 238 case 0: /* nop */ 239 if(trace) werrstr("nop"); 240 continue; 241 242 case 0x01: /* set location */ 243 s->loc = dwarfgetaddr(b); 244 if(trace) werrstr("loc = 0x%lx", s->loc); 245 if(locstop) 246 return 0; 247 continue; 248 249 case 0x02: /* advance loc1 */ 250 arg1 = dwarfget1(b); 251 goto advance; 252 253 case 0x03: /* advance loc2 */ 254 arg1 = dwarfget2(b); 255 goto advance; 256 257 case 0x04: /* advance loc4 */ 258 arg1 = dwarfget4(b); 259 goto advance; 260 261 case 0x05: /* offset extended */ 262 arg1 = dwarfget128(b); 263 arg2 = dwarfget128(b); 264 goto offset; 265 266 case 0x06: /* restore extended */ 267 arg1 = dwarfget128(b); 268 goto restore; 269 270 case 0x07: /* undefined */ 271 arg1 = dwarfget128(b); 272 if(trace) werrstr("r%ld = undef", arg1); 273 if(checkreg(s, arg1) < 0) 274 return -1; 275 s->r[arg1].type = RuleUndef; 276 continue; 277 278 case 0x08: /* same value */ 279 arg1 = dwarfget128(b); 280 if(trace) werrstr("r%ld = same", arg1); 281 if(checkreg(s, arg1) < 0) 282 return -1; 283 s->r[arg1].type = RuleSame; 284 continue; 285 286 case 0x09: /* register */ 287 arg1 = dwarfget128(b); 288 arg2 = dwarfget128(b); 289 if(trace) werrstr("r%ld = r%ld", arg1, arg2); 290 if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0) 291 return -1; 292 s->r[arg1].type = RuleRegister; 293 s->r[arg1].reg = arg2; 294 continue; 295 296 case 0x0A: /* remember state */ 297 e = malloc(s->nr*sizeof(e[0])); 298 if(trace) werrstr("push"); 299 if(e == nil) 300 return -1; 301 void *newstack = malloc(s->nstack*sizeof(s->stack[0])); 302 RtlMoveMemory(newstack, s->stack, s->nstack*sizeof(s->stack[0])); 303 if (newstack) { 304 free(s->stack); 305 s->stack = newstack; 306 } else { 307 free(e); 308 return -1; 309 } 310 if(b->p == nil){ 311 free(e); 312 return -1; 313 } 314 s->stack[s->nstack++] = e; 315 memmove(e, s->r, s->nr*sizeof(e[0])); 316 continue; 317 318 case 0x0B: /* restore state */ 319 if(trace) werrstr("pop"); 320 if(s->nstack == 0){ 321 werrstr("restore state underflow"); 322 return -1; 323 } 324 e = s->stack[s->nstack-1]; 325 memmove(s->r, e, s->nr*sizeof(e[0])); 326 free(e); 327 s->nstack--; 328 continue; 329 330 case 0x0C: /* def cfa */ 331 arg1 = dwarfget128(b); 332 arg2 = dwarfget128(b); 333 defcfa: 334 if(trace) werrstr("cfa %ld(r%ld)", arg2, arg1); 335 if(checkreg(s, arg1) < 0) 336 return -1; 337 s->cfa->type = RuleRegOff; 338 s->cfa->reg = arg1; 339 s->cfa->offset = arg2; 340 continue; 341 342 case 0x0D: /* def cfa register */ 343 arg1 = dwarfget128(b); 344 if(trace) werrstr("cfa reg r%ld", arg1); 345 if(s->cfa->type != RuleRegOff){ 346 werrstr("change CFA register but CFA not in register+offset form"); 347 return -1; 348 } 349 if(checkreg(s, arg1) < 0) 350 return -1; 351 s->cfa->reg = arg1; 352 continue; 353 354 case 0x0E: /* def cfa offset */ 355 arg1 = dwarfget128(b); 356 cfaoffset: 357 if(trace) werrstr("cfa off %ld", arg1); 358 if(s->cfa->type != RuleRegOff){ 359 werrstr("change CFA offset but CFA not in register+offset form"); 360 return -1; 361 } 362 s->cfa->offset = arg1; 363 continue; 364 365 case 0x0F: /* def cfa expression */ 366 if(trace) werrstr("cfa expr"); 367 s->cfa->type = RuleLocation; 368 s->cfa->loc.len = dwarfget128(b); 369 s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len); 370 continue; 371 372 case 0x10: /* def reg expression */ 373 arg1 = dwarfget128(b); 374 if(trace) werrstr("reg expr r%ld", arg1); 375 if(checkreg(s, arg1) < 0) 376 return -1; 377 s->r[arg1].type = RuleLocation; 378 s->r[arg1].loc.len = dwarfget128(b); 379 s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len); 380 continue; 381 382 case 0x11: /* offset extended */ 383 arg1 = dwarfget128(b); 384 arg2 = dwarfget128s(b); 385 goto offset; 386 387 case 0x12: /* cfa sf */ 388 arg1 = dwarfget128(b); 389 arg2 = dwarfget128s(b); 390 goto defcfa; 391 392 case 0x13: /* cfa offset sf */ 393 arg1 = dwarfget128s(b); 394 goto cfaoffset; 395 396 default: /* unknown */ 397 werrstr("unknown opcode 0x%x in cfa program", c); 398 return -1; 399 } 400 } 401 /* not reached */ 402 } 403 404 int dwarfcomputecfa(Dwarf *d, DwarfExpr *cfa, PROSSYM_REGISTERS registers, ulong *cfaLocation) 405 { 406 switch (cfa->type) { 407 case RuleRegOff: 408 *cfaLocation = registers->Registers[cfa->reg] + cfa->offset; 409 werrstr("cfa reg %d (%x) offset %x = %x", cfa->reg, (ulong)registers->Registers[cfa->reg], cfa->offset, cfaLocation); 410 break; 411 default: 412 werrstr("cfa->type %x", cfa->type); 413 return -1; 414 } 415 416 return 0; 417 } 418 419 int dwarfregunwind(Dwarf *d, ulong pc, ulong fde, DwarfExpr *cfa, PROSSYM_REGISTERS registers) 420 { 421 int i; 422 State s = { }; 423 DwarfExpr initr[sizeof(registers->Registers) / sizeof(registers->Registers[0])] = { }; 424 DwarfExpr r[sizeof(registers->Registers) / sizeof(registers->Registers[0])] = { }; 425 DwarfExpr ra; 426 427 int nr = s.nr = sizeof(registers->Registers) / sizeof(registers->Registers[0]); 428 s.initr = initr; 429 s.r = r; 430 for (i = 0; i < sizeof(r) / sizeof(r[0]); i++) { 431 r[i].type = RuleRegister; 432 r[i].offset = registers->Registers[i]; 433 r[i].reg = i; 434 } 435 436 int res = dwarfunwind(d, pc, cfa, &ra, initr, sizeof(initr) / sizeof(initr[0])); 437 if (res == -1) return -1; 438 439 ulong cfaLocation; 440 441 if (dwarfcomputecfa(d, cfa, registers, &cfaLocation) == -1) 442 return -1; 443 444 for (i = 0; i < nr; i++) { 445 switch (r[i].type) { 446 case RuleUndef: 447 werrstr("Undefined register slot %d", i); 448 assert(FALSE); 449 break; 450 case RuleSame: 451 break; 452 case RuleRegister: 453 registers->Registers[i] = registers->Registers[r[i].reg]; 454 break; 455 case RuleRegOff: { 456 BOOLEAN success = 457 RosSymCallbacks.MemGetProc 458 (d->pe->fd, 459 ®isters->Registers[i], 460 r[i].offset + registers->Registers[r[i].reg], 461 d->addrsize); 462 if (!success) return -1; 463 } break; 464 case RuleCfaOffset: 465 { 466 BOOLEAN success = 467 RosSymCallbacks.MemGetProc 468 (d->pe->fd, 469 ®isters->Registers[i], 470 r[i].offset + cfaLocation, 471 d->addrsize); 472 werrstr("reg[%d] = %x: cfa offset (cfa %x, offset %x) -> @%x", i, (ulong)registers->Registers[i], cfaLocation, initr[i].offset, r[i].offset + cfaLocation); 473 if (!success) return -1; 474 } break; 475 default: 476 werrstr("We don't yet support cfa rule %d in slot %d", r[i].type, i); 477 assert(FALSE); 478 break; 479 } 480 } 481 482 ulong cfaSpace[4]; 483 for (i = 0; i < sizeof(cfaSpace) / sizeof(cfaSpace[0]); i++) { 484 RosSymCallbacks.MemGetProc 485 (d->pe->fd, &cfaSpace[i], cfaLocation + (i * 4), d->addrsize); 486 } 487 werrstr("CFA(%x) [%08x, %08x, %08x, %08x]", 488 cfaLocation, cfaSpace[0], cfaSpace[1], cfaSpace[2], cfaSpace[3]); 489 490 return 0; 491 } 492