aboutsummaryrefslogtreecommitdiff
path: root/src/libmach/dwarfpc.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-04-19 19:29:25 +0000
committerrsc <devnull@localhost>2004-04-19 19:29:25 +0000
commita84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46 (patch)
tree59a0e921597e5aa53e83d487c16727a7bf01547a /src/libmach/dwarfpc.c
parent0e3cc9f456ea49919459bf1164d0c8309a6134fa (diff)
downloadplan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.gz
plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.tar.bz2
plan9port-a84cbb2a17c9d0b88c561d5b7cb50d79a19e7c46.zip
libmach
Diffstat (limited to 'src/libmach/dwarfpc.c')
-rw-r--r--src/libmach/dwarfpc.c338
1 files changed, 338 insertions, 0 deletions
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 <u.h>
+#include <libc.h>
+#include <bio.h>
+#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<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 = 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 = 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;
+}