[RXCE]
[reactos.git] / reactos / sdk / lib / rossym_new / dwarfcfa.c
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 &registers->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 &registers->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 }