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