xref: /reactos/sdk/lib/rossym/dwarfcfa.c (revision de5c4720)
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