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;
113 if(d->frame.data == nil){
114 werrstr("no frame debugging information");
119 b.ep = b.p + d->frame.len;
120 b.addrsize = d->addrsize;
122 b.addrsize = 4; /* where should i find this? */
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;
129 werrstr("bad length in cie/fde header");
134 if(id == 0xFFFFFFFF){ /* CIE */
135 vers = dwarfget1(&b);
136 if(vers != 1 && vers != 2 && vers != 3){
138 fprint(2, "unknown cie version %d (wanted 1-3)\n", vers);
141 aug = dwarfgetstring(&b);
144 fprint(2, "unknown augmentation: %s\n", aug);
147 s->iquantum = dwarfget128(&b);
148 s->dquantum = dwarfget128s(&b);
149 s->rareg = dwarfget128(&b);
150 if(s->rareg > s->nr){
151 werrstr("return address is register %d but only have %d registers",
158 base = dwarfgetaddr(&b);
159 size = dwarfgetaddr(&b);
163 s->endloc = base+size;
164 if(base <= pc && pc < base+size)
168 werrstr("cannot find call frame information for pc 0x%lux", pc);
174 checkreg(State *s, long r)
176 if(r < 0 || r >= s->nr){
177 werrstr("bad register number 0x%lux", r);
184 dexec(DwarfBuf *b, State *s, int locstop)
198 werrstr("ran out of instructions during cfa program");
199 if(trace) fprint(2, "%r\n");
202 if(trace) fprint(2, "+ loc=0x%lux op 0x%ux ", s->loc, c);
204 case 1: /* advance location */
207 if(trace) fprint(2, "loc += %ld\n", arg1*s->iquantum);
208 s->loc += arg1 * s->iquantum;
213 case 2: /* offset rule */
215 arg2 = dwarfget128(b);
217 if(trace) fprint(2, "r%ld += %ld\n", arg1, arg2*s->dquantum);
218 if(checkreg(s, arg1) < 0)
220 s->r[arg1].type = RuleCfaOffset;
221 s->r[arg1].offset = arg2 * s->dquantum;
224 case 3: /* restore initial setting */
227 if(trace) fprint(2, "r%ld = init\n", arg1);
228 if(checkreg(s, arg1) < 0)
230 s->r[arg1] = s->initr[arg1];
236 if(trace) fprint(2, "nop\n");
239 case 0x01: /* set location */
240 s->loc = dwarfgetaddr(b);
241 if(trace) fprint(2, "loc = 0x%lux\n", s->loc);
246 case 0x02: /* advance loc1 */
250 case 0x03: /* advance loc2 */
254 case 0x04: /* advance loc4 */
258 case 0x05: /* offset extended */
259 arg1 = dwarfget128(b);
260 arg2 = dwarfget128(b);
263 case 0x06: /* restore extended */
264 arg1 = dwarfget128(b);
267 case 0x07: /* undefined */
268 arg1 = dwarfget128(b);
269 if(trace) fprint(2, "r%ld = undef\n", arg1);
270 if(checkreg(s, arg1) < 0)
272 s->r[arg1].type = RuleUndef;
275 case 0x08: /* same value */
276 arg1 = dwarfget128(b);
277 if(trace) fprint(2, "r%ld = same\n", arg1);
278 if(checkreg(s, arg1) < 0)
280 s->r[arg1].type = RuleSame;
283 case 0x09: /* register */
284 arg1 = dwarfget128(b);
285 arg2 = dwarfget128(b);
286 if(trace) fprint(2, "r%ld = r%ld\n", arg1, arg2);
287 if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0)
289 s->r[arg1].type = RuleRegister;
290 s->r[arg1].reg = arg2;
293 case 0x0A: /* remember state */
294 e = malloc(s->nr*sizeof(e[0]));
295 if(trace) fprint(2, "push\n");
298 p = realloc(s->stack, (s->nstack+1)*sizeof(s->stack[0]));
303 s->stack[s->nstack++] = e;
304 memmove(e, s->r, s->nr*sizeof(e[0]));
307 case 0x0B: /* restore state */
308 if(trace) fprint(2, "pop\n");
310 werrstr("restore state underflow");
313 e = s->stack[s->nstack-1];
314 memmove(s->r, e, s->nr*sizeof(e[0]));
315 p = realloc(s->stack, (s->nstack-1)*sizeof(s->stack[0]));
322 case 0x0C: /* def cfa */
323 arg1 = dwarfget128(b);
324 arg2 = dwarfget128(b);
326 if(trace) fprint(2, "cfa %ld(r%ld)\n", arg2, arg1);
327 if(checkreg(s, arg1) < 0)
329 s->cfa->type = RuleRegOff;
331 s->cfa->offset = arg2;
334 case 0x0D: /* def cfa register */
335 arg1 = dwarfget128(b);
336 if(trace) fprint(2, "cfa reg r%ld\n", arg1);
337 if(s->cfa->type != RuleRegOff){
338 werrstr("change CFA register but CFA not in register+offset form");
341 if(checkreg(s, arg1) < 0)
346 case 0x0E: /* def cfa offset */
347 arg1 = dwarfget128(b);
349 if(trace) fprint(2, "cfa off %ld\n", arg1);
350 if(s->cfa->type != RuleRegOff){
351 werrstr("change CFA offset but CFA not in register+offset form");
354 s->cfa->offset = arg1;
357 case 0x0F: /* def cfa expression */
358 if(trace) fprint(2, "cfa expr\n");
359 s->cfa->type = RuleLocation;
360 s->cfa->loc.len = dwarfget128(b);
361 s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len);
364 case 0x10: /* def reg expression */
365 arg1 = dwarfget128(b);
366 if(trace) fprint(2, "reg expr r%ld\n", arg1);
367 if(checkreg(s, arg1) < 0)
369 s->r[arg1].type = RuleLocation;
370 s->r[arg1].loc.len = dwarfget128(b);
371 s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len);
374 case 0x11: /* offset extended */
375 arg1 = dwarfget128(b);
376 arg2 = dwarfget128s(b);
379 case 0x12: /* cfa sf */
380 arg1 = dwarfget128(b);
381 arg2 = dwarfget128s(b);
384 case 0x13: /* cfa offset sf */
385 arg1 = dwarfget128s(b);
388 default: /* unknown */
389 werrstr("unknown opcode 0x%ux in cfa program", c);
393 return -1; /* not reached */