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