int dwarfnextsymat(Dwarf *d, DwarfSym *parent, DwarfSym *child) { uint sib; if (!parent->attrs.haskids || !parent->childoff) return -1; child->unit = parent->unit; child->aoff = parent->aoff; child->depth = parent->depth + 1; if(child->attrs.have.sibling){ sib = child->attrs.sibling; if(sib < d->info.len && d->info.data+sib > child->b.p) child->b.p = d->info.data+sib; else if (sib >= d->info.len) { werrstr("sibling reported as out of bounds %d vs %d", sib, d->info.len); return -1; } else if (d->info.data+sib+parent->unit < child->b.p) { werrstr("subsequent sibling is listed before prev %d vs %d", sib+parent->unit, child->b.p - d->info.data); return -1; } } // Uninitialized if (!child->b.d) { child->b = parent->b; child->b.p = parent->childoff + parent->b.d->info.data; werrstr("Rewound to childoff %x\n", parent->childoff); } return dwarfnextsym(d, child); }
static int _dwarfnametounit(Dwarf *d, char *name, DwarfBlock *bl, DwarfSym *s) { int vers; ulong len, unit, off; uchar *next; char *str; DwarfBuf b; b.d = d; b.p = bl->data; b.ep = b.p + bl->len; while(b.p < b.ep){ len = dwarfget4(&b); if(len > b.ep-b.p){ werrstr("bad length in dwarf name header"); return -1; } next = b.p + len; vers = dwarfget2(&b); if(vers != 1 && vers != 2){ werrstr("bad version %d in dwarf name header", vers); return -1; } unit = dwarfget4(&b); dwarfget4(&b); /* unit length */ while(b.p < next){ off = dwarfget4(&b); if(off == 0) break; str = dwarfgetstring(&b); if(strcmp(str, name) == 0){ if(dwarfenumunit(d, unit, s) < 0) return -1; if(unit + off >= s->b.ep - d->info.data){ werrstr("bad offset in name entry"); return -1; } s->b.p = d->info.data + unit + off; if(dwarfnextsym(d, s) < 0) return -1; if(s->attrs.name==nil || strcmp(s->attrs.name, name)!=0){ werrstr("unexpected name %#q in lookup for %#q", s->attrs.name, name); return -1; } return 0; } } b.p = next; } werrstr("unknown name '%s'", name); return -1; }
int dwarflookupchildtag(Dwarf *d, DwarfSym *parent, ulong tag, DwarfSym *s) { int rsym = dwarfnextsymat(d, parent, s); while (rsym == 0 && s->attrs.tag != tag) { if (s->attrs.haskids) { DwarfSym p = *s; int csym = dwarflookupchildtag(d, &p, tag, s); if (csym == 0) { return csym; } } rsym = dwarfnextsym(d, s); } return rsym; }
int dwarflookuptag(Dwarf *d, ulong unit, ulong tag, DwarfSym *s) { DwarfSym compunit = { }; if (dwarfenumunit(d, unit, &compunit) < 0) { return -1; } do { if (compunit.attrs.tag == tag) { *s = compunit; return 0; } if (dwarflookupchildtag(d, &compunit, tag, s) == 0) return 0; } while(dwarfnextsym(d, &compunit) == 0); werrstr("symbol with tag 0x%lux not found", tag); return -1; }
int dwarfnextsym(Dwarf *d, DwarfSym *s) { ulong num; DwarfAbbrev *a; werrstr("sym at %x (left %x)\n", s->b.p - d->info.data, s->b.ep - s->b.p); num = dwarfget128(&s->b); werrstr("abbrev num %x\n", num); s->num = num; if(num == 0){ return -1; } a = dwarfgetabbrev(d, s->aoff, num); werrstr("a %p\n", a); if(a == nil){ werrstr("getabbrev %x %x for %x", s->aoff, num, s->unit); return -1; } if(parseattrs(d, &s->b, s->attrs.tag, s->unit, a, &s->attrs) < 0) { return -1; } if (s->attrs.haskids) { DwarfSym childSkip = { }; s->childoff = s->b.p - d->info.data; werrstr("Set childoff at %x\n", s->childoff); int r = dwarfnextsymat(d, s, &childSkip); while (r == 0) { r = dwarfnextsym(d, &childSkip); } s->b = childSkip.b; } else { s->childoff = 0; } return 0; }
int dwarfenumunit(Dwarf *d, ulong unit, DwarfSym *s) { int i; ulong aoff, len; if(unit >= d->info.len){ werrstr("dwarf unit address 0x%x >= 0x%x out of range", unit, d->info.len); return -1; } memset(s, 0, sizeof *s); memset(&s->b, 0, sizeof s->b); s->b.d = d; s->b.p = d->info.data + unit; s->b.ep = d->info.data + d->info.len; len = dwarfget4(&s->b); s->unit = unit; s->nextunit = unit + 4 + len; s->b.ep = d->info.data + s->nextunit; if(s->b.ep - s->b.p < len){ badheader: werrstr("bad dwarf unit header at unit 0x%lux end %x start %x len %x", unit, s->b.ep - d->info.data, s->b.p - d->info.data, len); return -1; } s->b.ep = s->b.p+len; if((i=dwarfget2(&s->b)) > 4) goto badheader; aoff = dwarfget4(&s->b); s->b.addrsize = dwarfget1(&s->b); if(d->addrsize == 0) d->addrsize = s->b.addrsize; if(s->b.p == nil) goto badheader; s->aoff = aoff; return dwarfnextsym(d, s); }
void main(int argc, char **argv) { int c; Pe *pe; Dwarf *d; DwarfSym s; char *cdir, *dir, *file; ulong line, mtime, length; if(argc != 2) usage(); #if 0 fmtinstall('R', exprfmt); fmtinstall('H', encodefmt); #endif if((pe = peopen(argv[1])) == nil) sysfatal("elfopen %s: %r", argv[1]); if((d=dwarfopen(pe)) == nil) sysfatal("dwarfopen: %r"); if(dwarfenum(d, &s) < 0) sysfatal("dwarfenumall: %r"); while(dwarfnextsym(d, &s) == 1){ switch(s.attrs.tag){ case TagCompileUnit: print("compileunit %s\n", s.attrs.name); break; case TagSubprogram: c = 't'; goto sym; case TagVariable: c = 'd'; goto sym; case TagConstant: c = 'c'; goto sym; case TagFormalParameter: if(!s.attrs.name) break; c = 'p'; sym: if(s.attrs.isexternal) c += 'A' - 'a'; print("%c %s", c, s.attrs.name); if(s.attrs.have.lowpc) print(" 0x%lux-0x%lux", s.attrs.lowpc, s.attrs.highpc); switch(s.attrs.have.location){ case TBlock: print(" @ %.*H", s.attrs.location.b.len, s.attrs.location.b.data); break; case TConstant: print(" @ 0x%lux", s.attrs.location.c); break; } if(s.attrs.have.ranges) print(" ranges@0x%lux", s.attrs.ranges); print("\n"); if(s.attrs.have.lowpc){ if(dwarfpctoline(d, s.attrs.lowpc, &cdir, &dir, &file, &line, &mtime, &length) < 0) print("\tcould not find source: %r\n"); else if(dir == nil) print("\t%s/%s:%lud mtime=%lud length=%lud\n", cdir, file, line, mtime, length); else print("\t%s/%s/%s:%lud mtime=%lud length=%lud\n", cdir, dir, file, line, mtime, length); if(0) printrules(d, s.attrs.lowpc); if(0) printrules(d, (s.attrs.lowpc+s.attrs.highpc)/2); } break; } } exits(0); }
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; }