int dwarfpctoline(Dwarf *d, DwarfSym *proc, ulong pc, char **file, char **function, ulong *line) { char *cdir; uchar *prog, *opcount, *end, *dirs; ulong off, unit, len, vers, x, start, lastline; int i, first, firstline, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf; char *files, *s; DwarfBuf b; DwarfSym sym; State emit, cur, reset; char **f, **newf; f = nil; memset(proc, 0, sizeof(*proc)); int runit = dwarfaddrtounit(d, pc, &unit); if (runit < 0) return -1; int rtag = dwarflookuptag(d, unit, TagCompileUnit, &sym); if (rtag < 0) return -1; if(!sym.attrs.have.stmtlist){ werrstr("no line mapping information for 0x%x", pc); return -1; } off = sym.attrs.stmtlist; if(off >= d->line.len){ werrstr("bad stmtlist"); goto bad; } if(trace) werrstr("unit 0x%x stmtlist 0x%x", unit, sym.attrs.stmtlist); memset(&b, 0, sizeof b); b.d = d; b.p = d->line.data + off; b.ep = b.p + d->line.len; b.addrsize = sym.b.addrsize; /* should i get this from somewhere else? */ len = dwarfget4(&b); if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){ werrstr("bad len"); goto bad; } b.ep = b.p+len; vers = dwarfget2(&b); if(vers != 2){ werrstr("bad dwarf version 0x%x", vers); return -1; } len = dwarfget4(&b); if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){ werrstr("another bad len"); goto bad; } prog = b.p+len; quantum = dwarfget1(&b); isstmt = dwarfget1(&b); linebase = (schar)dwarfget1(&b); linerange = (schar)dwarfget1(&b); opcodebase = dwarfget1(&b); opcount = b.p-1; dwarfgetnref(&b, opcodebase-1); if(b.p == nil){ werrstr("bad opcode chart"); goto bad; } /* just skip the files and dirs for now; we'll come back */ dirs = b.p; while (b.p && *b.p) dwarfgetstring(&b); dwarfget1(&b); files = (char*)b.p; while(b.p!=nil && *b.p!=0){ dwarfgetstring(&b); dwarfget128(&b); dwarfget128(&b); dwarfget128(&b); } dwarfget1(&b); /* move on to the program */ if(b.p == nil || b.p > prog){ werrstr("bad header"); goto bad; } b.p = prog; reset.addr = 0; reset.file = 1; reset.line = 1; reset.column = 0; reset.flags = isstmt ? Isstmt : 0; reset.isa = 0; cur = reset; emit = reset; nf = 0; start = 0; if(trace) werrstr("program @ %lu ... %.*H opbase = %d", b.p - d->line.data, b.ep-b.p, b.p, opcodebase); first = 1; while(b.p != nil){ firstline = 0; op = dwarfget1(&b); if(trace) werrstr("\tline %lu, addr 0x%x, op %d %.10H", cur.line, cur.addr, op, b.p); if(op >= opcodebase){ a = (op - opcodebase) / linerange; l = (op - opcodebase) % linerange + linebase; cur.line += l; cur.addr += a * quantum; if(trace) werrstr(" +%d,%d", a, l); emit: if(first){ if(cur.addr > pc){ werrstr("found wrong line mapping 0x%x for pc 0x%x", cur.addr, pc); /* This is an overzealous check. gcc can produce discontiguous ranges and reorder statements, so it's possible for a future line to start ahead of pc and still find a matching one. */ /*goto out;*/ firstline = 1; } first = 0; start = cur.addr; } if(cur.addr > pc && !firstline) break; if(b.p == nil){ werrstr("buffer underflow in line mapping"); goto out; } emit = cur; if(emit.flags & EndSequence){ werrstr("found wrong line mapping 0x%x-0x%x for pc 0x%x", start, cur.addr, pc); goto out; } cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin); }else{ switch(op){ case 0: /* extended op code */ if(trace) werrstr(" ext"); len = dwarfget128(&b); end = b.p+len; if(b.p == nil || end > b.ep || end < b.p || len < 1) goto bad; switch(dwarfget1(&b)){ case 1: /* end sequence */ if(trace) werrstr(" end"); cur.flags |= EndSequence; goto emit; case 2: /* set address */ cur.addr = dwarfgetaddr(&b); if(trace) werrstr(" set pc 0x%x", cur.addr); break; case 3: /* define file */ newf = malloc(nf+1*sizeof(f[0])); if (newf) RtlMoveMemory(newf, f, nf*sizeof(f[0])); if(newf == nil) goto out; free(f); f = newf; f[nf++] = s = dwarfgetstring(&b); DPRINT1("str %s", s); dwarfget128(&b); dwarfget128(&b); dwarfget128(&b); if(trace) werrstr(" def file %s", s); break; } if(b.p == nil || b.p > end) goto bad; b.p = end; break; case 1: /* emit */ if(trace) werrstr(" emit"); goto emit; case 2: /* advance pc */ a = dwarfget128(&b); if(trace) werrstr(" advance pc + %lu", a*quantum); cur.addr += a * quantum; break; case 3: /* advance line */ l = dwarfget128s(&b); if(trace) werrstr(" advance line + %ld", l); cur.line += l; break; case 4: /* set file */ if(trace) werrstr(" set file"); cur.file = dwarfget128s(&b); break; case 5: /* set column */ if(trace) werrstr(" set column"); cur.column = dwarfget128(&b); break; case 6: /* negate stmt */ if(trace) werrstr(" negate stmt"); cur.flags ^= Isstmt; break; case 7: /* set basic block */ if(trace) werrstr(" set basic block"); cur.flags |= BasicDwarfBlock; break; case 8: /* const add pc */ a = (255 - opcodebase) / linerange * quantum; if(trace) werrstr(" const add pc + %d", a); cur.addr += a; break; case 9: /* fixed advance pc */ a = dwarfget2(&b); if(trace) werrstr(" fixed advance pc + %d", a); cur.addr += a; break; case 10: /* set prologue end */ if(trace) werrstr(" set prologue end"); cur.flags |= PrologueEnd; break; case 11: /* set epilogue begin */ if(trace) werrstr(" set epilogue begin"); cur.flags |= EpilogueBegin; break; case 12: /* set isa */ if(trace) werrstr(" set isa"); cur.isa = dwarfget128(&b); break; default: /* something new - skip it */ if(trace) werrstr(" unknown %d", opcount[op]); for(i=0; i<opcount[op]; i++) dwarfget128(&b); break; } } } if(b.p == nil) goto bad; /* finally! the data we seek is in "emit" */ if(emit.file == 0){ werrstr("invalid file index in mapping data"); goto out; } if(line) *line = emit.line; /* skip over first emit.file-2 guys */ b.p = (uchar*)files; for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){ dwarfgetstring(&b); dwarfget128(&b); dwarfget128(&b); dwarfget128(&b); } if(b.p == nil){ werrstr("problem parsing file data second time (cannot happen)"); goto bad; } if(*b.p == 0){ if(i >= nf){ werrstr("bad file index in mapping data"); goto bad; } b.p = (uchar*)f[i]; } s = dwarfgetstring(&b); *file = s; i = dwarfget128(&b); /* directory */ x = dwarfget128(&b); x = dwarfget128(&b); /* fetch dir name */ cdir = sym.attrs.have.compdir ? sym.attrs.compdir : 0; char *dwarfdir; dwarfdir = nil; b.p = dirs; for (x = 1; b.p && *b.p; x++) { dwarfdir = dwarfgetstring(&b); if (x == i) break; } if (!cdir && dwarfdir) cdir = dwarfdir; char *filefull = malloc(strlen(cdir) + strlen(*file) + 2); strcpy(filefull, cdir); strcat(filefull, "/"); strcat(filefull, *file); *file = filefull; *function = nil; lastline = 0; runit = dwarfaddrtounit(d, pc, &unit); if (runit == 0) { DwarfSym compunit = { }; int renum = dwarfenumunit(d, unit, &compunit); if (renum < 0) return -1; renum = dwarfnextsymat(d, &compunit, proc); while (renum == 0) { if (proc->attrs.tag == TagSubprogram && proc->attrs.have.name) { if (proc->attrs.lowpc <= pc && proc->attrs.highpc > pc) { *function = malloc(strlen(proc->attrs.name)+1); strcpy(*function, proc->attrs.name); goto done; } } renum = dwarfnextsym(d, proc); } } // Next search by declaration runit = dwarfaddrtounit(d, pc, &unit); if (runit == 0) { DwarfSym compunit = { }; int renum = dwarfenumunit(d, unit, &compunit); if (renum < 0) return -1; renum = dwarfnextsymat(d, &compunit, proc); while (renum == 0) { if (proc->attrs.tag == TagSubprogram && proc->attrs.have.name && proc->attrs.declfile == emit.file) { if (proc->attrs.declline <= *line && proc->attrs.declline > lastline) { free(*function); *function = malloc(strlen(proc->attrs.name)+1); strcpy(*function, proc->attrs.name); goto done; } lastline = proc->attrs.declline; } renum = dwarfnextsym(d, proc); } } /* free at last, free at last */ done: free(f); return 0; bad: werrstr("corrupted line mapping for 0x%x", pc); out: free(f); return -1; }
static int dexec(DwarfBuf *b, State *s, int locstop) { int c; long arg1, arg2; DwarfExpr *e; 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) werrstr("%r\n"); return -1; } if(trace) werrstr("+ loc=0x%lux op 0x%ux ", s->loc, c); switch(c>>6){ case 1: /* advance location */ arg1 = c&0x3F; advance: if(trace) werrstr("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) werrstr("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) werrstr("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) werrstr("nop\n"); continue; case 0x01: /* set location */ s->loc = dwarfgetaddr(b); if(trace) werrstr("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) werrstr("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) werrstr("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) werrstr("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) werrstr("push\n"); if(e == nil) return -1; void *newstack = malloc(s->nstack*sizeof(s->stack[0])); RtlMoveMemory(newstack, s->stack, s->nstack*sizeof(s->stack[0])); if (newstack) { free(s->stack); s->stack = newstack; } else { free(e); return -1; } if(b->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) werrstr("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])); free(e); s->nstack--; continue; case 0x0C: /* def cfa */ arg1 = dwarfget128(b); arg2 = dwarfget128(b); defcfa: if(trace) werrstr("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) werrstr("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) werrstr("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) werrstr("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) werrstr("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; } } /* not reached */ }
int dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, ulong *line, ulong *mtime, ulong *length) { uchar *prog, *opcount, *end; ulong off, unit, len, vers, x, start; int i, first, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf; char *files, *dirs, *s; DwarfBuf b; DwarfSym sym; State emit, cur, reset; uchar **f, **newf; f = nil; if(dwarfaddrtounit(d, pc, &unit) < 0 || dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0) return -1; if(!sym.attrs.have.stmtlist){ werrstr("no line mapping information for 0x%lux", pc); return -1; } off = sym.attrs.stmtlist; if(off >= d->line.len){ fprint(2, "bad stmtlist\n"); goto bad; } if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist); memset(&b, 0, sizeof b); b.d = d; b.p = d->line.data + off; b.ep = b.p + d->line.len; b.addrsize = sym.b.addrsize; /* should i get this from somewhere else? */ len = dwarfget4(&b); if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){ fprint(2, "bad len\n"); goto bad; } b.ep = b.p+len; vers = dwarfget2(&b); if(vers != 2){ werrstr("bad dwarf version 0x%lux", vers); return -1; } len = dwarfget4(&b); if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){ fprint(2, "another bad len\n"); goto bad; } prog = b.p+len; quantum = dwarfget1(&b); isstmt = dwarfget1(&b); linebase = (schar)dwarfget1(&b); linerange = (schar)dwarfget1(&b); opcodebase = dwarfget1(&b); opcount = b.p-1; dwarfgetnref(&b, opcodebase-1); if(b.p == nil){ fprint(2, "bad opcode chart\n"); goto bad; } /* just skip the files and dirs for now; we'll come back */ dirs = (char*)b.p; while(b.p!=nil && *b.p!=0) dwarfgetstring(&b); dwarfget1(&b); files = (char*)b.p; while(b.p!=nil && *b.p!=0){ dwarfgetstring(&b); dwarfget128(&b); dwarfget128(&b); dwarfget128(&b); } dwarfget1(&b); /* move on to the program */ if(b.p == nil || b.p > prog){ fprint(2, "bad header\n"); goto bad; } b.p = prog; reset.addr = 0; reset.file = 1; reset.line = 1; reset.column = 0; reset.flags = isstmt ? Isstmt : 0; reset.isa = 0; cur = reset; emit = reset; nf = 0; start = 0; if(trace) fprint(2, "program @ %lud ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase); first = 1; while(b.p != nil){ op = dwarfget1(&b); if(trace) fprint(2, "\tline %lud, addr 0x%lux, op %d %.10H", cur.line, cur.addr, op, b.p); if(op >= opcodebase){ a = (op - opcodebase) / linerange; l = (op - opcodebase) % linerange + linebase; cur.line += l; cur.addr += a * quantum; if(trace) fprint(2, " +%d,%d\n", a, l); emit: if(first){ if(cur.addr > pc){ werrstr("found wrong line mapping 0x%lux for pc 0x%lux", cur.addr, pc); goto out; } first = 0; start = cur.addr; } if(cur.addr > pc) break; if(b.p == nil){ werrstr("buffer underflow in line mapping"); goto out; } emit = cur; if(emit.flags & EndSequence){ werrstr("found wrong line mapping 0x%lux-0x%lux for pc 0x%lux", start, cur.addr, pc); goto out; } cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin); }else{ switch(op){ case 0: /* extended op code */ if(trace) fprint(2, " ext"); len = dwarfget128(&b); end = b.p+len; if(b.p == nil || end > b.ep || end < b.p || len < 1) goto bad; switch(dwarfget1(&b)){ case 1: /* end sequence */ if(trace) fprint(2, " end\n"); cur.flags |= EndSequence; goto emit; case 2: /* set address */ cur.addr = dwarfgetaddr(&b); if(trace) fprint(2, " set pc 0x%lux\n", cur.addr); break; case 3: /* define file */ newf = realloc(f, (nf+1)*sizeof(f[0])); if(newf == nil) goto out; f = newf; f[nf++] = b.p; s = dwarfgetstring(&b); dwarfget128(&b); dwarfget128(&b); dwarfget128(&b); if(trace) fprint(2, " def file %s\n", s); break; } if(b.p == nil || b.p > end) goto bad; b.p = end; break; case 1: /* emit */ if(trace) fprint(2, " emit\n"); goto emit; case 2: /* advance pc */ a = dwarfget128(&b); if(trace) fprint(2, " advance pc + %lud\n", a*quantum); cur.addr += a * quantum; break; case 3: /* advance line */ l = dwarfget128s(&b); if(trace) fprint(2, " advance line + %ld\n", l); cur.line += l; break; case 4: /* set file */ if(trace) fprint(2, " set file\n"); cur.file = dwarfget128s(&b); break; case 5: /* set column */ if(trace) fprint(2, " set column\n"); cur.column = dwarfget128(&b); break; case 6: /* negate stmt */ if(trace) fprint(2, " negate stmt\n"); cur.flags ^= Isstmt; break; case 7: /* set basic block */ if(trace) fprint(2, " set basic block\n"); cur.flags |= BasicDwarfBlock; break; case 8: /* const add pc */ a = (255 - opcodebase) / linerange * quantum; if(trace) fprint(2, " const add pc + %d\n", a); cur.addr += a; break; case 9: /* fixed advance pc */ a = dwarfget2(&b); if(trace) fprint(2, " fixed advance pc + %d\n", a); cur.addr += a; break; case 10: /* set prologue end */ if(trace) fprint(2, " set prologue end\n"); cur.flags |= PrologueEnd; break; case 11: /* set epilogue begin */ if(trace) fprint(2, " set epilogue begin\n"); cur.flags |= EpilogueBegin; break; case 12: /* set isa */ if(trace) fprint(2, " set isa\n"); cur.isa = dwarfget128(&b); break; default: /* something new - skip it */ if(trace) fprint(2, " unknown %d\n", opcount[op]); for(i=0; i<opcount[op]; i++) dwarfget128(&b); break; } } } if(b.p == nil) goto bad; /* finally! the data we seek is in "emit" */ if(emit.file == 0){ werrstr("invalid file index in mapping data"); goto out; } if(line) *line = emit.line; /* skip over first emit.file-2 guys */ b.p = (uchar*)files; for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){ dwarfgetstring(&b); dwarfget128(&b); dwarfget128(&b); dwarfget128(&b); } if(b.p == nil){ werrstr("problem parsing file data second time (cannot happen)"); goto bad; } if(*b.p == 0){ if(i >= nf){ werrstr("bad file index in mapping data"); goto bad; } b.p = f[i]; } s = dwarfgetstring(&b); if(file) *file = s; i = dwarfget128(&b); /* directory */ x = dwarfget128(&b); if(mtime) *mtime = x; x = dwarfget128(&b); if(length) *length = x; /* fetch dir name */ if(cdir) *cdir = sym.attrs.compdir; if(dir){ if(i == 0) *dir = nil; else{ b.p = (uchar*)dirs; for(i--; i>0 && b.p!=nil && *b.p!=0; i--) dwarfgetstring(&b); if(b.p==nil || *b.p==0){ werrstr("bad directory reference in line mapping"); goto out; /* can only happen with bad dir index */ } *dir = dwarfgetstring(&b); } } /* free at last, free at last */ free(f); return 0; bad: werrstr("corrupted line mapping for 0x%lux", pc); out: free(f); return -1; }