plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

dwarfpc.c (7715B)


      1 /*
      2  * Dwarf pc to source line conversion.
      3  *
      4  * Maybe should do the reverse here, but what should the interface look like?
      5  * One possibility is to use the Plan 9 line2addr interface:
      6  *
      7  *	long line2addr(ulong line, ulong basepc)
      8  *
      9  * which returns the smallest pc > basepc with line number line (ignoring file name).
     10  *
     11  * The encoding may be small, but it sure isn't simple!
     12  */
     13 
     14 #include <u.h>
     15 #include <libc.h>
     16 #include <bio.h>
     17 #include "elf.h"
     18 #include "dwarf.h"
     19 
     20 #define trace 0
     21 
     22 enum
     23 {
     24 	Isstmt = 1<<0,
     25 	BasicDwarfBlock = 1<<1,
     26 	EndSequence = 1<<2,
     27 	PrologueEnd = 1<<3,
     28 	EpilogueBegin = 1<<4
     29 };
     30 
     31 typedef struct State State;
     32 struct State
     33 {
     34 	ulong addr;
     35 	ulong file;
     36 	ulong line;
     37 	ulong column;
     38 	ulong flags;
     39 	ulong isa;
     40 };
     41 
     42 int
     43 dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, ulong *line, ulong *mtime, ulong *length)
     44 {
     45 	uchar *prog, *opcount, *end;
     46 	ulong off, unit, len, vers, x, start;
     47 	int i, first, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf;
     48 	char *files, *dirs, *s;
     49 	DwarfBuf b;
     50 	DwarfSym sym;
     51 	State emit, cur, reset;
     52 	uchar **f, **newf;
     53 
     54 	f = nil;
     55 
     56 	if(dwarfaddrtounit(d, pc, &unit) < 0
     57 	|| dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0)
     58 		return -1;
     59 
     60 	if(!sym.attrs.have.stmtlist){
     61 		werrstr("no line mapping information for 0x%lux", pc);
     62 		return -1;
     63 	}
     64 	off = sym.attrs.stmtlist;
     65 	if(off >= d->line.len){
     66 		fprint(2, "bad stmtlist\n");
     67 		goto bad;
     68 	}
     69 
     70 	if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist);
     71 
     72 	memset(&b, 0, sizeof b);
     73 	b.d = d;
     74 	b.p = d->line.data + off;
     75 	b.ep = b.p + d->line.len;
     76 	b.addrsize = sym.b.addrsize;	/* should i get this from somewhere else? */
     77 
     78 	len = dwarfget4(&b);
     79 	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
     80 		fprint(2, "bad len\n");
     81 		goto bad;
     82 	}
     83 
     84 	b.ep = b.p+len;
     85 	vers = dwarfget2(&b);
     86 	if(vers != 2){
     87 		werrstr("bad dwarf version 0x%lux", vers);
     88 		return -1;
     89 	}
     90 
     91 	len = dwarfget4(&b);
     92 	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
     93 		fprint(2, "another bad len\n");
     94 		goto bad;
     95 	}
     96 	prog = b.p+len;
     97 
     98 	quantum = dwarfget1(&b);
     99 	isstmt = dwarfget1(&b);
    100 	linebase = (schar)dwarfget1(&b);
    101 	linerange = (schar)dwarfget1(&b);
    102 	opcodebase = dwarfget1(&b);
    103 
    104 	opcount = b.p-1;
    105 	dwarfgetnref(&b, opcodebase-1);
    106 	if(b.p == nil){
    107 		fprint(2, "bad opcode chart\n");
    108 		goto bad;
    109 	}
    110 
    111 	/* just skip the files and dirs for now; we'll come back */
    112 	dirs = (char*)b.p;
    113 	while(b.p!=nil && *b.p!=0)
    114 		dwarfgetstring(&b);
    115 	dwarfget1(&b);
    116 
    117 	files = (char*)b.p;
    118 	while(b.p!=nil && *b.p!=0){
    119 		dwarfgetstring(&b);
    120 		dwarfget128(&b);
    121 		dwarfget128(&b);
    122 		dwarfget128(&b);
    123 	}
    124 	dwarfget1(&b);
    125 
    126 	/* move on to the program */
    127 	if(b.p == nil || b.p > prog){
    128 		fprint(2, "bad header\n");
    129 		goto bad;
    130 	}
    131 	b.p = prog;
    132 
    133 	reset.addr = 0;
    134 	reset.file = 1;
    135 	reset.line = 1;
    136 	reset.column = 0;
    137 	reset.flags = isstmt ? Isstmt : 0;
    138 	reset.isa = 0;
    139 
    140 	cur = reset;
    141 	emit = reset;
    142 	nf = 0;
    143 	start = 0;
    144 	if(trace) fprint(2, "program @ %lud ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
    145 	first = 1;
    146 	while(b.p != nil){
    147 		op = dwarfget1(&b);
    148 		if(trace) fprint(2, "\tline %lud, addr 0x%lux, op %d %.10H", cur.line, cur.addr, op, b.p);
    149 		if(op >= opcodebase){
    150 			a = (op - opcodebase) / linerange;
    151 			l = (op - opcodebase) % linerange + linebase;
    152 			cur.line += l;
    153 			cur.addr += a * quantum;
    154 			if(trace) fprint(2, " +%d,%d\n", a, l);
    155 		emit:
    156 			if(first){
    157 				if(cur.addr > pc){
    158 					werrstr("found wrong line mapping 0x%lux for pc 0x%lux", cur.addr, pc);
    159 					goto out;
    160 				}
    161 				first = 0;
    162 				start = cur.addr;
    163 			}
    164 			if(cur.addr > pc)
    165 				break;
    166 			if(b.p == nil){
    167 				werrstr("buffer underflow in line mapping");
    168 				goto out;
    169 			}
    170 			emit = cur;
    171 			if(emit.flags & EndSequence){
    172 				werrstr("found wrong line mapping 0x%lux-0x%lux for pc 0x%lux", start, cur.addr, pc);
    173 				goto out;
    174 			}
    175 			cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
    176 		}else{
    177 			switch(op){
    178 			case 0:	/* extended op code */
    179 				if(trace) fprint(2, " ext");
    180 				len = dwarfget128(&b);
    181 				end = b.p+len;
    182 				if(b.p == nil || end > b.ep || end < b.p || len < 1)
    183 					goto bad;
    184 				switch(dwarfget1(&b)){
    185 				case 1:	/* end sequence */
    186 					if(trace) fprint(2, " end\n");
    187 					cur.flags |= EndSequence;
    188 					goto emit;
    189 				case 2:	/* set address */
    190 					cur.addr = dwarfgetaddr(&b);
    191 					if(trace) fprint(2, " set pc 0x%lux\n", cur.addr);
    192 					break;
    193 				case 3:	/* define file */
    194 					newf = realloc(f, (nf+1)*sizeof(f[0]));
    195 					if(newf == nil)
    196 						goto out;
    197 					f = newf;
    198 					f[nf++] = b.p;
    199 					s = dwarfgetstring(&b);
    200 					dwarfget128(&b);
    201 					dwarfget128(&b);
    202 					dwarfget128(&b);
    203 					if(trace) fprint(2, " def file %s\n", s);
    204 					break;
    205 				}
    206 				if(b.p == nil || b.p > end)
    207 					goto bad;
    208 				b.p = end;
    209 				break;
    210 			case 1:	/* emit */
    211 				if(trace) fprint(2, " emit\n");
    212 				goto emit;
    213 			case 2:	/* advance pc */
    214 				a = dwarfget128(&b);
    215 				if(trace) fprint(2, " advance pc + %lud\n", a*quantum);
    216 				cur.addr += a * quantum;
    217 				break;
    218 			case 3:	/* advance line */
    219 				l = dwarfget128s(&b);
    220 				if(trace) fprint(2, " advance line + %ld\n", l);
    221 				cur.line += l;
    222 				break;
    223 			case 4:	/* set file */
    224 				if(trace) fprint(2, " set file\n");
    225 				cur.file = dwarfget128s(&b);
    226 				break;
    227 			case 5:	/* set column */
    228 				if(trace) fprint(2, " set column\n");
    229 				cur.column = dwarfget128(&b);
    230 				break;
    231 			case 6:	/* negate stmt */
    232 				if(trace) fprint(2, " negate stmt\n");
    233 				cur.flags ^= Isstmt;
    234 				break;
    235 			case 7:	/* set basic block */
    236 				if(trace) fprint(2, " set basic block\n");
    237 				cur.flags |= BasicDwarfBlock;
    238 				break;
    239 			case 8:	/* const add pc */
    240 				a = (255 - opcodebase) / linerange * quantum;
    241 				if(trace) fprint(2, " const add pc + %d\n", a);
    242 				cur.addr += a;
    243 				break;
    244 			case 9:	/* fixed advance pc */
    245 				a = dwarfget2(&b);
    246 				if(trace) fprint(2, " fixed advance pc + %d\n", a);
    247 				cur.addr += a;
    248 				break;
    249 			case 10:	/* set prologue end */
    250 				if(trace) fprint(2, " set prologue end\n");
    251 				cur.flags |= PrologueEnd;
    252 				break;
    253 			case 11:	/* set epilogue begin */
    254 				if(trace) fprint(2, " set epilogue begin\n");
    255 				cur.flags |= EpilogueBegin;
    256 				break;
    257 			case 12:	/* set isa */
    258 				if(trace) fprint(2, " set isa\n");
    259 				cur.isa = dwarfget128(&b);
    260 				break;
    261 			default:	/* something new - skip it */
    262 				if(trace) fprint(2, " unknown %d\n", opcount[op]);
    263 				for(i=0; i<opcount[op]; i++)
    264 					dwarfget128(&b);
    265 				break;
    266 			}
    267 		}
    268 	}
    269 	if(b.p == nil)
    270 		goto bad;
    271 
    272 	/* finally!  the data we seek is in "emit" */
    273 
    274 	if(emit.file == 0){
    275 		werrstr("invalid file index in mapping data");
    276 		goto out;
    277 	}
    278 	if(line)
    279 		*line = emit.line;
    280 
    281 	/* skip over first emit.file-2 guys */
    282 	b.p = (uchar*)files;
    283 	for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
    284 		dwarfgetstring(&b);
    285 		dwarfget128(&b);
    286 		dwarfget128(&b);
    287 		dwarfget128(&b);
    288 	}
    289 	if(b.p == nil){
    290 		werrstr("problem parsing file data second time (cannot happen)");
    291 		goto bad;
    292 	}
    293 	if(*b.p == 0){
    294 		if(i >= nf){
    295 			werrstr("bad file index in mapping data");
    296 			goto bad;
    297 		}
    298 		b.p = f[i];
    299 	}
    300 	s = dwarfgetstring(&b);
    301 	if(file)
    302 		*file = s;
    303 	i = dwarfget128(&b);		/* directory */
    304 	x = dwarfget128(&b);
    305 	if(mtime)
    306 		*mtime = x;
    307 	x = dwarfget128(&b);
    308 	if(length)
    309 		*length = x;
    310 
    311 	/* fetch dir name */
    312 	if(cdir)
    313 		*cdir = sym.attrs.compdir;
    314 
    315 	if(dir){
    316 		if(i == 0)
    317 			*dir = nil;
    318 		else{
    319 			b.p = (uchar*)dirs;
    320 			for(i--; i>0 && b.p!=nil && *b.p!=0; i--)
    321 				dwarfgetstring(&b);
    322 			if(b.p==nil || *b.p==0){
    323 				werrstr("bad directory reference in line mapping");
    324 				goto out;		/* can only happen with bad dir index */
    325 			}
    326 			*dir = dwarfgetstring(&b);
    327 		}
    328 	}
    329 
    330 	/* free at last, free at last */
    331 	free(f);
    332 	return 0;
    333 
    334 bad:
    335 	werrstr("corrupted line mapping for 0x%lux", pc);
    336 out:
    337 	free(f);
    338 	return -1;
    339 }