2 * Dwarf call frame unwinding.
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
18 typedef struct State State;
38 static int findfde(Dwarf*, ulong, State*, DwarfBuf*);
39 static int dexec(DwarfBuf*, State*, int);
42 dwarfunwind(Dwarf *d, ulong pc, DwarfExpr *cfa, DwarfExpr *ra, DwarfExpr *r, int nr)
49 initr = mallocz(nr*sizeof(initr[0]), 1);
53 memset(&s, 0, sizeof s);
60 if(findfde(d, pc, &s, &fde) < 0){
65 memset(r, 0, nr*sizeof(r[0]));
68 if(trace) fprint(2, "s.init %p-%p, fde %p-%p\n", s.init.p, s.init.ep, fde.p, fde.ep);
70 if(dexec(&b, &s, 0) < 0)
74 memmove(initr, r, nr*sizeof(initr[0]));
76 if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
78 if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
79 if(dexec(&fde, &s, 1) < 0)
91 for(i=0; i<s.nstack; i++)
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.
104 findfde(Dwarf *d, ulong pc, State *s, DwarfBuf *fde)
110 ulong len, id, base, size;
115 b.ep = b.p + d->frame.len;
116 b.addrsize = d->addrsize;
118 b.addrsize = 4; /* where should i find this? */
120 for(; b.p < b.ep; b.p = next){
121 if((i = (b.p - d->frame.data) % b.addrsize))
122 b.p += b.addrsize - i;
125 werrstr("bad length in cie/fde header");
130 if(id == 0xFFFFFFFF){ /* CIE */
131 vers = dwarfget1(&b);
132 if(vers != 1 && vers != 2 && vers != 3){
134 fprint(2, "unknown cie version %d (wanted 1-3)\n", vers);
137 aug = dwarfgetstring(&b);
140 fprint(2, "unknown augmentation: %s\n", aug);
143 s->iquantum = dwarfget128(&b);
144 s->dquantum = dwarfget128s(&b);
145 s->rareg = dwarfget128(&b);
146 if(s->rareg > s->nr){
147 werrstr("return address is register %d but only have %d registers",
154 base = dwarfgetaddr(&b);
155 size = dwarfgetaddr(&b);
159 s->endloc = base+size;
160 if(base <= pc && pc < base+size)
164 werrstr("cannot find call frame information for pc 0x%lux", pc);
170 checkreg(State *s, long r)
172 if(r < 0 || r >= s->nr){
173 werrstr("bad register number 0x%lux", r);
180 dexec(DwarfBuf *b, State *s, int locstop)
194 werrstr("ran out of instructions during cfa program");
195 if(trace) fprint(2, "%r\n");
198 if(trace) fprint(2, "+ loc=0x%lux op 0x%ux ", s->loc, c);
200 case 1: /* advance location */
203 if(trace) fprint(2, "loc += %ld\n", arg1*s->iquantum);
204 s->loc += arg1 * s->iquantum;
209 case 2: /* offset rule */
211 arg2 = dwarfget128(b);
213 if(trace) fprint(2, "r%ld += %ld\n", arg1, arg2*s->dquantum);
214 if(checkreg(s, arg1) < 0)
216 s->r[arg1].type = RuleCfaOffset;
217 s->r[arg1].offset = arg2 * s->dquantum;
220 case 3: /* restore initial setting */
223 if(trace) fprint(2, "r%ld = init\n", arg1);
224 if(checkreg(s, arg1) < 0)
226 s->r[arg1] = s->initr[arg1];
232 if(trace) fprint(2, "nop\n");
235 case 0x01: /* set location */
236 s->loc = dwarfgetaddr(b);
237 if(trace) fprint(2, "loc = 0x%lux\n", s->loc);
242 case 0x02: /* advance loc1 */
246 case 0x03: /* advance loc2 */
250 case 0x04: /* advance loc4 */
254 case 0x05: /* offset extended */
255 arg1 = dwarfget128(b);
256 arg2 = dwarfget128(b);
259 case 0x06: /* restore extended */
260 arg1 = dwarfget128(b);
263 case 0x07: /* undefined */
264 arg1 = dwarfget128(b);
265 if(trace) fprint(2, "r%ld = undef\n", arg1);
266 if(checkreg(s, arg1) < 0)
268 s->r[arg1].type = RuleUndef;
271 case 0x08: /* same value */
272 arg1 = dwarfget128(b);
273 if(trace) fprint(2, "r%ld = same\n", arg1);
274 if(checkreg(s, arg1) < 0)
276 s->r[arg1].type = RuleSame;
279 case 0x09: /* register */
280 arg1 = dwarfget128(b);
281 arg2 = dwarfget128(b);
282 if(trace) fprint(2, "r%ld = r%ld\n", arg1, arg2);
283 if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0)
285 s->r[arg1].type = RuleRegister;
286 s->r[arg1].reg = arg2;
289 case 0x0A: /* remember state */
290 e = malloc(s->nr*sizeof(e[0]));
291 if(trace) fprint(2, "push\n");
294 p = realloc(s->stack, (s->nstack+1)*sizeof(s->stack[0]));
299 s->stack[s->nstack++] = e;
300 memmove(e, s->r, s->nr*sizeof(e[0]));
303 case 0x0B: /* restore state */
304 if(trace) fprint(2, "pop\n");
306 werrstr("restore state underflow");
309 e = s->stack[s->nstack-1];
310 memmove(s->r, e, s->nr*sizeof(e[0]));
311 p = realloc(s->stack, (s->nstack-1)*sizeof(s->stack[0]));
318 case 0x0C: /* def cfa */
319 arg1 = dwarfget128(b);
320 arg2 = dwarfget128(b);
322 if(trace) fprint(2, "cfa %ld(r%ld)\n", arg2, arg1);
323 if(checkreg(s, arg1) < 0)
325 s->cfa->type = RuleRegOff;
327 s->cfa->offset = arg2;
330 case 0x0D: /* def cfa register */
331 arg1 = dwarfget128(b);
332 if(trace) fprint(2, "cfa reg r%ld\n", arg1);
333 if(s->cfa->type != RuleRegOff){
334 werrstr("change CFA register but CFA not in register+offset form");
337 if(checkreg(s, arg1) < 0)
342 case 0x0E: /* def cfa offset */
343 arg1 = dwarfget128(b);
345 if(trace) fprint(2, "cfa off %ld\n", arg1);
346 if(s->cfa->type != RuleRegOff){
347 werrstr("change CFA offset but CFA not in register+offset form");
350 s->cfa->offset = arg1;
353 case 0x0F: /* def cfa expression */
354 if(trace) fprint(2, "cfa expr\n");
355 s->cfa->type = RuleLocation;
356 s->cfa->loc.len = dwarfget128(b);
357 s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len);
360 case 0x10: /* def reg expression */
361 arg1 = dwarfget128(b);
362 if(trace) fprint(2, "reg expr r%ld\n", arg1);
363 if(checkreg(s, arg1) < 0)
365 s->r[arg1].type = RuleLocation;
366 s->r[arg1].loc.len = dwarfget128(b);
367 s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len);
370 case 0x11: /* offset extended */
371 arg1 = dwarfget128(b);
372 arg2 = dwarfget128s(b);
375 case 0x12: /* cfa sf */
376 arg1 = dwarfget128(b);
377 arg2 = dwarfget128s(b);
380 case 0x13: /* cfa offset sf */
381 arg1 = dwarfget128s(b);
384 default: /* unknown */
385 werrstr("unknown opcode 0x%ux in cfa program", c);
389 return -1; /* not reached */