@global dwunwind, dwunwind1, decoderules ; { @local uwns; uwns = @names c32le { enum { Nreg = 67, /* # dwarf regs in amd64 abi */ CFA = Nreg, /* index of CFA in Rule row */ Nrow = Nreg+1, /* # entries in Rule row */ }; typedef enum Rulekind { Rnone = 0, // cfa rules Rcfaro, Rcfae, /* unimplemented */ // register rules Rsame, Roff, Rvoff, Rreg, Rexp, /* unimplemented */ Rvexp, /* unimplemented */ } Rulekind; }; /* return pointer to copy of data pointed to by P consistent with type of *P */ @defloc cop(p) { return {@typeof(p)}getbytes(p); } // @defloc showrule(r) // { // printf("rule: kind=%e r=%d n=%d\n", // (uwns`Rulekind)r.kind, // r.r, // r.n); // } @defloc applyunwind(rules, getreg, setreg, get, spno) { @local cfa, i, r, needsp; @defloc evalcfa() { @local r; r = rules[uwns`CFA]; switch((uwns`Rulekind)r.kind){ case uwns`Rcfaro: return getreg(r.r)+r.n; case uwns`Rcfae: error("unwind: rule Rcfae is unimplemented"); default: error("unwind: bug"); } } cfa = evalcfa(); needsp = 0; for(i = 0; i < uwns`Nreg; i++){ r = rules[i]; switch((uwns`Rulekind)r.kind){ case uwns`Rnone: if(i == spno) needsp = 1; break; case uwns`Rsame: setreg(i, getreg(i)); break; case uwns`Roff: setreg(i, get(cfa+r.n)); break; case uwns`Rvoff: setreg(i, cfa+r.n); break; case uwns`Rreg: setreg(i, getreg(r.r)); break; case uwns`Rexp: case uwns`Rvexp: error("unwind: rule %e is unimplemented", (enum uwns`Rulekind)r.kind); default: error("unwind: bad unwind rule: %d\n", r.kind); } } if(needsp) setreg(spno, cfa); } @defloc mkuwctx32(dom, ctx) { @local new; /* copy ctx */ new = {@typeof(ctx)}getbytes(ctx); @defloc g(r) { switch(r){ case 0: return ctx->rax; case 1: return ctx->rcx; case 2: return ctx->rdx; case 3: return ctx->rbx; case 4: return ctx->rsp; case 5: return ctx->rbp; case 6: return ctx->rsi; case 7: return ctx->rdi; case 8: return ctx->rip; default: error("getreg on bad register %d", r); } } @defloc s(r, v) { switch(r){ case 0: return new->rax = v; case 1: return new->rcx = v; case 2: return new->rdx = v; case 3: return new->rbx = v; case 4: return new->rsp = v; case 5: return new->rbp = v; case 6: return new->rsi = v; case 7: return new->rdi = v; case 8: return new->rip = v; default: error("putreg on bad register %d", r); } } @defloc m(a) { return *(uintptr*){dom}a; } return [g, s, m, new, 4]; } @defloc mkuwctx64(dom, ctx) { @local new; /* copy ctx */ new = {@typeof(ctx)}getbytes(ctx); @defloc g(r) { switch(r){ case 0: return ctx->rax; case 1: return ctx->rdx; case 2: return ctx->rcx; case 3: return ctx->rbx; case 4: return ctx->rsi; case 5: return ctx->rdi; case 6: return ctx->rbp; case 7: return ctx->rsp; case 8: return ctx->r8; case 9: return ctx->r9; case 10: return ctx->r10; case 11: return ctx->r11; case 12: return ctx->r12; case 13: return ctx->r13; case 14: return ctx->r14; case 15: return ctx->r15; case 16: return ctx->rip; case 49: return ctx->eflags; case 50: return ctx->es; case 51: return ctx->cs; case 52: return ctx->ss; case 53: return ctx->ds; case 54: return ctx->fs; case 55: return ctx->gs; case 58: return ctx->fs_base; case 59: return ctx->gs_base; default: error("get on bad register %d", r); } } @defloc s(r, v) { switch(r){ case 0: return new->rax = v; case 1: return new->rdx = v; case 2: return new->rcx = v; case 3: return new->rbx = v; case 4: return new->rsi = v; case 5: return new->rdi = v; case 6: return new->rbp = v; case 7: return new->rsp = v; case 8: return new->r8 = v; case 9: return new->r9 = v; case 10: return new->r10 = v; case 11: return new->r11 = v; case 12: return new->r12 = v; case 13: return new->r13 = v; case 14: return new->r14 = v; case 15: return new->r15 = v; case 16: return new->rip = v; case 49: return new->eflags = v; case 50: return new->es = v; case 51: return new->cs = v; case 52: return new->ss = v; case 53: return new->ds = v; case 54: return new->fs = v; case 55: return new->gs = v; case 58: return new->fs_base = v; case 59: return new->gs_base = v; default: error("get on bad register %d", r); } } @defloc m(a) { return *(uintptr*){dom}a; } return [g, s, m, new, 7]; } @define dwunwind1(ctx, as, nsmap) { @local fp, dom, ns, rs, mkuwctx; @local g, s, m, new, spno; if(ctx->pc == 0) return nil; ns = nsmap.byaddr(ctx->pc); if(ns == nil) error("no name space for pc %p", ctx->pc); dom = mkdom(ns, as); rs = ns.unwind1(ctx->pc); if(rs == nil) goto guess; mkuwctx = sizeof(ns`void*) == 8 ? mkuwctx64 : mkuwctx32; [g, s, m, new, spno] = mkuwctx(dom, ctx); applyunwind(rs, g, s, m, spno); return new; guess: if(ctx->fp == 0) return nil; fp = (uintptr*){dom}ctx->fp; ctx->pc = *(fp+1); ctx->fp = *fp; return ctx; } @define dwunwind(ctx, as, nsmap) { @local xs; @local first; xs = []; first = 1; while(ctx != nil && ctx->pc){ if(!first) /* attempt to set pc close to calling address; see dwarf version 3, section 6.4.4. */ ctx->pc -= 1; else first = 0; append(xs, cop(ctx)); ctx = dwunwind1(ctx, as, nsmap); } return xs; } }