xref: /reactos/sdk/lib/rossym_new/dwarfcfa.c (revision 98e8827a)
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 }
492