diff options
author | rsc <devnull@localhost> | 2004-04-19 19:29:25 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2004-04-19 19:29:25 +0000 |
commit | a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46 (patch) | |
tree | 59a0e921597e5aa53e83d487c16727a7bf01547a /src/libmach/dwarfcfa.c | |
parent | 0e3cc9f456ea49919459bf1164d0c8309a6134fa (diff) | |
download | plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.gz plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.bz2 plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.zip |
libmach
Diffstat (limited to 'src/libmach/dwarfcfa.c')
-rw-r--r-- | src/libmach/dwarfcfa.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/src/libmach/dwarfcfa.c b/src/libmach/dwarfcfa.c new file mode 100644 index 00000000..de342cd7 --- /dev/null +++ b/src/libmach/dwarfcfa.c @@ -0,0 +1,391 @@ +/* + * Dwarf call frame unwinding. + * + * The call frame unwinding values are encoded using a state machine + * like the pc<->line mapping, but it's a different machine. + * The expressions to generate the old values are similar in function to the + * ``dwarf expressions'' used for locations in the code, but of course not + * the same encoding. + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include "elf.h" +#include "dwarf.h" + +#define trace 0 + +typedef struct State State; +struct State +{ + ulong loc; + ulong endloc; + ulong iquantum; + ulong dquantum; + char *augmentation; + int version; + ulong rareg; + DwarfBuf init; + DwarfExpr *cfa; + DwarfExpr *ra; + DwarfExpr *r; + DwarfExpr *initr; + int nr; + DwarfExpr **stack; + int nstack; +}; + +static int findfde(Dwarf*, ulong, State*, DwarfBuf*); +static int dexec(DwarfBuf*, State*, int); + +int +dwarfunwind(Dwarf *d, ulong pc, DwarfExpr *cfa, DwarfExpr *ra, DwarfExpr *r, int nr) +{ + int i, ret; + DwarfBuf fde, b; + DwarfExpr *initr; + State s; + + initr = mallocz(nr*sizeof(initr[0]), 1); + if(initr == 0) + return -1; + + memset(&s, 0, sizeof s); + s.loc = 0; + s.cfa = cfa; + s.ra = ra; + s.r = r; + s.nr = nr; + + if(findfde(d, pc, &s, &fde) < 0){ + free(initr); + return -1; + } + + memset(r, 0, nr*sizeof(r[0])); + for(i=0; i<nr; i++) + r[i].type = RuleSame; + if(trace) fprint(2, "s.init %p-%p, fde %p-%p\n", s.init.p, s.init.ep, fde.p, fde.ep); + b = s.init; + if(dexec(&b, &s, 0) < 0) + goto err; + + s.initr = initr; + memmove(initr, r, nr*sizeof(initr[0])); + + if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc); + while(s.loc < pc){ + if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc); + if(dexec(&fde, &s, 1) < 0) + goto err; + } + *ra = s.r[s.rareg]; + + ret = 0; + goto out; + +err: + ret = -1; +out: + free(initr); + for(i=0; i<s.nstack; i++) + free(s.stack[i]); + free(s.stack); + return ret; +} + +/* + * XXX This turns out to be much more expensive than the actual + * running of the machine in dexec. It probably makes sense to + * cache the last 10 or so fde's we've found, since stack traces + * will keep asking for the same info over and over. + */ +static int +findfde(Dwarf *d, ulong pc, State *s, DwarfBuf *fde) +{ + static int nbad; + char *aug; + uchar *next; + int i, vers; + ulong len, id, base, size; + DwarfBuf b; + + b.d = d; + b.p = d->frame.data; + b.ep = b.p + d->frame.len; + b.addrsize = d->addrsize; + if(b.addrsize == 0) + b.addrsize = 4; /* where should i find this? */ + + for(; b.p < b.ep; b.p = next){ + if((i = (b.p - d->frame.data) % b.addrsize)) + b.p += b.addrsize - i; + len = dwarfget4(&b); + if(len > b.ep-b.p){ + werrstr("bad length in cie/fde header"); + return -1; + } + next = b.p+len; + id = dwarfget4(&b); + if(id == 0xFFFFFFFF){ /* CIE */ + vers = dwarfget1(&b); + if(vers != 1 && vers != 2 && vers != 3){ + if(++nbad == 1) + fprint(2, "unknown cie version %d (wanted 1-3)\n", vers); + continue; + } + aug = dwarfgetstring(&b); + if(aug && *aug){ + if(++nbad == 1) + fprint(2, "unknown augmentation: %s\n", aug); + continue; + } + s->iquantum = dwarfget128(&b); + s->dquantum = dwarfget128s(&b); + s->rareg = dwarfget128(&b); + if(s->rareg > s->nr){ + werrstr("return address is register %d but only have %d registers", + s->rareg, s->nr); + return -1; + } + s->init.p = b.p; + s->init.ep = next; + }else{ /* FDE */ + base = dwarfgetaddr(&b); + size = dwarfgetaddr(&b); + fde->p = b.p; + fde->ep = next; + s->loc = base; + s->endloc = base+size; + if(base <= pc && pc < base+size) + return 0; + } + } + werrstr("cannot find call frame information for pc 0x%lux", pc); + return -1; + +} + +static int +checkreg(State *s, long r) +{ + if(r < 0 || r >= s->nr){ + werrstr("bad register number 0x%lux", r); + return -1; + } + return 0; +} + +static int +dexec(DwarfBuf *b, State *s, int locstop) +{ + int c; + long arg1, arg2; + DwarfExpr *e, **p; + + for(;;){ + if(b->p == b->ep){ + if(s->initr) + s->loc = s->endloc; + return 0; + } + c = dwarfget1(b); + if(b->p == nil){ + werrstr("ran out of instructions during cfa program"); + if(trace) fprint(2, "%r\n"); + return -1; + } + if(trace) fprint(2, "+ loc=0x%lux op 0x%ux ", s->loc, c); + switch(c>>6){ + case 1: /* advance location */ + arg1 = c&0x3F; + advance: + if(trace) fprint(2, "loc += %ld\n", arg1*s->iquantum); + s->loc += arg1 * s->iquantum; + if(locstop) + return 0; + continue; + + case 2: /* offset rule */ + arg1 = c&0x3F; + arg2 = dwarfget128(b); + offset: + if(trace) fprint(2, "r%ld += %ld\n", arg1, arg2*s->dquantum); + if(checkreg(s, arg1) < 0) + return -1; + s->r[arg1].type = RuleCfaOffset; + s->r[arg1].offset = arg2 * s->dquantum; + continue; + + case 3: /* restore initial setting */ + arg1 = c&0x3F; + restore: + if(trace) fprint(2, "r%ld = init\n", arg1); + if(checkreg(s, arg1) < 0) + return -1; + s->r[arg1] = s->initr[arg1]; + continue; + } + + switch(c){ + case 0: /* nop */ + if(trace) fprint(2, "nop\n"); + continue; + + case 0x01: /* set location */ + s->loc = dwarfgetaddr(b); + if(trace) fprint(2, "loc = 0x%lux\n", s->loc); + if(locstop) + return 0; + continue; + + case 0x02: /* advance loc1 */ + arg1 = dwarfget1(b); + goto advance; + + case 0x03: /* advance loc2 */ + arg1 = dwarfget2(b); + goto advance; + + case 0x04: /* advance loc4 */ + arg1 = dwarfget4(b); + goto advance; + + case 0x05: /* offset extended */ + arg1 = dwarfget128(b); + arg2 = dwarfget128(b); + goto offset; + + case 0x06: /* restore extended */ + arg1 = dwarfget128(b); + goto restore; + + case 0x07: /* undefined */ + arg1 = dwarfget128(b); + if(trace) fprint(2, "r%ld = undef\n", arg1); + if(checkreg(s, arg1) < 0) + return -1; + s->r[arg1].type = RuleUndef; + continue; + + case 0x08: /* same value */ + arg1 = dwarfget128(b); + if(trace) fprint(2, "r%ld = same\n", arg1); + if(checkreg(s, arg1) < 0) + return -1; + s->r[arg1].type = RuleSame; + continue; + + case 0x09: /* register */ + arg1 = dwarfget128(b); + arg2 = dwarfget128(b); + if(trace) fprint(2, "r%ld = r%ld\n", arg1, arg2); + if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0) + return -1; + s->r[arg1].type = RuleRegister; + s->r[arg1].reg = arg2; + continue; + + case 0x0A: /* remember state */ + e = malloc(s->nr*sizeof(e[0])); + if(trace) fprint(2, "push\n"); + if(e == nil) + return -1; + p = realloc(s->stack, (s->nstack+1)*sizeof(s->stack[0])); + if(p == nil){ + free(e); + return -1; + } + s->stack[s->nstack++] = e; + memmove(e, s->r, s->nr*sizeof(e[0])); + continue; + + case 0x0B: /* restore state */ + if(trace) fprint(2, "pop\n"); + if(s->nstack == 0){ + werrstr("restore state underflow"); + return -1; + } + e = s->stack[s->nstack-1]; + memmove(s->r, e, s->nr*sizeof(e[0])); + p = realloc(s->stack, (s->nstack-1)*sizeof(s->stack[0])); + if(p == nil) + return -1; + free(e); + s->nstack--; + continue; + + case 0x0C: /* def cfa */ + arg1 = dwarfget128(b); + arg2 = dwarfget128(b); + defcfa: + if(trace) fprint(2, "cfa %ld(r%ld)\n", arg2, arg1); + if(checkreg(s, arg1) < 0) + return -1; + s->cfa->type = RuleRegOff; + s->cfa->reg = arg1; + s->cfa->offset = arg2; + continue; + + case 0x0D: /* def cfa register */ + arg1 = dwarfget128(b); + if(trace) fprint(2, "cfa reg r%ld\n", arg1); + if(s->cfa->type != RuleRegOff){ + werrstr("change CFA register but CFA not in register+offset form"); + return -1; + } + if(checkreg(s, arg1) < 0) + return -1; + s->cfa->reg = arg1; + continue; + + case 0x0E: /* def cfa offset */ + arg1 = dwarfget128(b); + cfaoffset: + if(trace) fprint(2, "cfa off %ld\n", arg1); + if(s->cfa->type != RuleRegOff){ + werrstr("change CFA offset but CFA not in register+offset form"); + return -1; + } + s->cfa->offset = arg1; + continue; + + case 0x0F: /* def cfa expression */ + if(trace) fprint(2, "cfa expr\n"); + s->cfa->type = RuleLocation; + s->cfa->loc.len = dwarfget128(b); + s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len); + continue; + + case 0x10: /* def reg expression */ + arg1 = dwarfget128(b); + if(trace) fprint(2, "reg expr r%ld\n", arg1); + if(checkreg(s, arg1) < 0) + return -1; + s->r[arg1].type = RuleLocation; + s->r[arg1].loc.len = dwarfget128(b); + s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len); + continue; + + case 0x11: /* offset extended */ + arg1 = dwarfget128(b); + arg2 = dwarfget128s(b); + goto offset; + + case 0x12: /* cfa sf */ + arg1 = dwarfget128(b); + arg2 = dwarfget128s(b); + goto defcfa; + + case 0x13: /* cfa offset sf */ + arg1 = dwarfget128s(b); + goto cfaoffset; + + default: /* unknown */ + werrstr("unknown opcode 0x%ux in cfa program", c); + return -1; + } + } + return -1; /* not reached */ +} + |