From a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46 Mon Sep 17 00:00:00 2001 From: rsc Date: Mon, 19 Apr 2004 19:29:25 +0000 Subject: libmach --- src/libmach/dwarfpc.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 src/libmach/dwarfpc.c (limited to 'src/libmach/dwarfpc.c') diff --git a/src/libmach/dwarfpc.c b/src/libmach/dwarfpc.c new file mode 100644 index 00000000..165b3aa0 --- /dev/null +++ b/src/libmach/dwarfpc.c @@ -0,0 +1,338 @@ +/* + * Dwarf pc to source line conversion. + * + * Maybe should do the reverse here, but what should the interface look like? + * One possibility is to use the Plan 9 line2addr interface: + * + * long line2addr(ulong line, ulong basepc) + * + * which returns the smallest pc > basepc with line number line (ignoring file name). + * + * The encoding may be small, but it sure isn't simple! + */ + +#include +#include +#include +#include "elf.h" +#include "dwarf.h" + +#define trace 0 + +enum +{ + Isstmt = 1<<0, + BasicDwarfBlock = 1<<1, + EndSequence = 1<<2, + PrologueEnd = 1<<3, + EpilogueBegin = 1<<4, +}; + +typedef struct State State; +struct State +{ + ulong addr; + ulong file; + ulong line; + ulong column; + ulong flags; + ulong isa; +}; + +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 = b.p; + while(b.p!=nil && *b.p!=0) + dwarfgetstring(&b); + dwarfget1(&b); + + files = 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[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 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 = 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; +} -- cgit v1.2.3