338 lines · 7.5 KB · C
RawDownload
/* * 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;}; intdwarfpctoline(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[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;}