xref: /reactos/sdk/lib/rossym/dwarfpc.c (revision d6eebaa4)
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 <ntddk.h>
15 #include <reactos/rossym.h>
16 #include "rossympriv.h"
17 #include <ntimage.h>
18 
19 #define NDEBUG
20 #include <debug.h>
21 
22 #include "dwarf.h"
23 #include "pe.h"
24 
25 #define trace 0
26 
27 enum
28 {
29 	Isstmt = 1<<0,
30 	BasicDwarfBlock = 1<<1,
31 	EndSequence = 1<<2,
32 	PrologueEnd = 1<<3,
33 	EpilogueBegin = 1<<4
34 };
35 
36 typedef struct State State;
37 struct State
38 {
39 	ulong addr;
40 	ulong file;
41 	ulong line;
42 	ulong column;
43 	ulong flags;
44 	ulong isa;
45 };
46 
47 int
48 dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, char **function, ulong *line, ulong *mtime, ulong *length)
49 {
50 	uchar *prog, *opcount, *end, *dirs;
51 	ulong off, unit, len, vers, x, start, lastline;
52 	int i, first, firstline, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf;
53 	char *files, *s;
54 	DwarfBuf b;
55 	DwarfSym sym;
56 	State emit, cur, reset;
57 	uchar **f, **newf;
58 
59 	f = nil;
60 
61 	if(dwarfaddrtounit(d, pc, &unit) < 0
62 	   || dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0)
63 		return -1;
64 
65 	if(!sym.attrs.have.stmtlist){
66 		werrstr("no line mapping information for 0x%x", pc);
67 		return -1;
68 	}
69 	off = sym.attrs.stmtlist;
70 	if(off >= d->line.len){
71 		werrstr("bad stmtlist\n");
72 		goto bad;
73 	}
74 
75 	if(trace) werrstr("unit 0x%x stmtlist 0x%x", unit, sym.attrs.stmtlist);
76 
77 	memset(&b, 0, sizeof b);
78 	b.d = d;
79 	b.p = d->line.data + off;
80 	b.ep = b.p + d->line.len;
81 	b.addrsize = sym.b.addrsize;	/* should i get this from somewhere else? */
82 
83 	len = dwarfget4(&b);
84 	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
85 		werrstr("bad len\n");
86 		goto bad;
87 	}
88 
89 	b.ep = b.p+len;
90 	vers = dwarfget2(&b);
91 	if(vers != 2){
92 		werrstr("bad dwarf version 0x%x", vers);
93 		return -1;
94 	}
95 
96 	len = dwarfget4(&b);
97 	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
98 		werrstr("another bad len\n");
99 		goto bad;
100 	}
101 	prog = b.p+len;
102 
103 	quantum = dwarfget1(&b);
104 	isstmt = dwarfget1(&b);
105 	linebase = (schar)dwarfget1(&b);
106 	linerange = (schar)dwarfget1(&b);
107 	opcodebase = dwarfget1(&b);
108 
109 	opcount = b.p-1;
110 	dwarfgetnref(&b, opcodebase-1);
111 	if(b.p == nil){
112 		werrstr("bad opcode chart\n");
113 		goto bad;
114 	}
115 
116 	/* just skip the files and dirs for now; we'll come back */
117 	dirs = b.p;
118 	while (b.p && *b.p)
119 		dwarfgetstring(&b);
120 	dwarfget1(&b);
121 
122 	files = (char*)b.p;
123 	while(b.p!=nil && *b.p!=0){
124 		dwarfgetstring(&b);
125 		dwarfget128(&b);
126 		dwarfget128(&b);
127 		dwarfget128(&b);
128 	}
129 	dwarfget1(&b);
130 
131 	/* move on to the program */
132 	if(b.p == nil || b.p > prog){
133 		werrstr("bad header\n");
134 		goto bad;
135 	}
136 	b.p = prog;
137 
138 	reset.addr = 0;
139 	reset.file = 1;
140 	reset.line = 1;
141 	reset.column = 0;
142 	reset.flags = isstmt ? Isstmt : 0;
143 	reset.isa = 0;
144 
145 	cur = reset;
146 	emit = reset;
147 	nf = 0;
148 	start = 0;
149 	if(trace) werrstr("program @ %lu ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
150 	first = 1;
151 	while(b.p != nil){
152 		firstline = 0;
153 		op = dwarfget1(&b);
154 		if(trace) werrstr("\tline %lu, addr 0x%x, op %d %.10H", cur.line, cur.addr, op, b.p);
155 		if(op >= opcodebase){
156 			a = (op - opcodebase) / linerange;
157 			l = (op - opcodebase) % linerange + linebase;
158 			cur.line += l;
159 			cur.addr += a * quantum;
160 			if(trace) werrstr(" +%d,%d\n", a, l);
161 		emit:
162 			if(first){
163 				if(cur.addr > pc){
164 					werrstr("found wrong line mapping 0x%x for pc 0x%x", cur.addr, pc);
165 					/* This is an overzealous check.  gcc can produce discontiguous ranges
166 					   and reorder statements, so it's possible for a future line to start
167 					   ahead of pc and still find a matching one. */
168 					/*goto out;*/
169 					firstline = 1;
170 				}
171 				first = 0;
172 				start = cur.addr;
173 			}
174 			if(cur.addr > pc && !firstline)
175 				break;
176 			if(b.p == nil){
177 				werrstr("buffer underflow in line mapping");
178 				goto out;
179 			}
180 			emit = cur;
181 			if(emit.flags & EndSequence){
182 				werrstr("found wrong line mapping 0x%x-0x%x for pc 0x%x", start, cur.addr, pc);
183 				goto out;
184 			}
185 			cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
186 		}else{
187 			switch(op){
188 			case 0:	/* extended op code */
189 				if(trace) werrstr(" ext");
190 				len = dwarfget128(&b);
191 				end = b.p+len;
192 				if(b.p == nil || end > b.ep || end < b.p || len < 1)
193 					goto bad;
194 				switch(dwarfget1(&b)){
195 				case 1:	/* end sequence */
196 					if(trace) werrstr(" end\n");
197 					cur.flags |= EndSequence;
198 					goto emit;
199 				case 2:	/* set address */
200 					cur.addr = dwarfgetaddr(&b);
201 					if(trace) werrstr(" set pc 0x%x\n", cur.addr);
202 					break;
203 				case 3:	/* define file */
204 					newf = malloc(nf+1*sizeof(f[0]));
205 					if (newf)
206 						RtlMoveMemory(newf, f, nf*sizeof(f[0]));
207 					if(newf == nil)
208 						goto out;
209 					f[nf++] = b.p;
210 					s = dwarfgetstring(&b);
211 					dwarfget128(&b);
212 					dwarfget128(&b);
213 					dwarfget128(&b);
214 					if(trace) werrstr(" def file %s\n", s);
215 					break;
216 				}
217 				if(b.p == nil || b.p > end)
218 					goto bad;
219 				b.p = end;
220 				break;
221 			case 1:	/* emit */
222 				if(trace) werrstr(" emit\n");
223 				goto emit;
224 			case 2:	/* advance pc */
225 				a = dwarfget128(&b);
226 				if(trace) werrstr(" advance pc + %lu\n", a*quantum);
227 				cur.addr += a * quantum;
228 				break;
229 			case 3:	/* advance line */
230 				l = dwarfget128s(&b);
231 				if(trace) werrstr(" advance line + %ld\n", l);
232 				cur.line += l;
233 				break;
234 			case 4:	/* set file */
235 				if(trace) werrstr(" set file\n");
236 				cur.file = dwarfget128s(&b);
237 				break;
238 			case 5:	/* set column */
239 				if(trace) werrstr(" set column\n");
240 				cur.column = dwarfget128(&b);
241 				break;
242 			case 6:	/* negate stmt */
243 				if(trace) werrstr(" negate stmt\n");
244 				cur.flags ^= Isstmt;
245 				break;
246 			case 7:	/* set basic block */
247 				if(trace) werrstr(" set basic block\n");
248 				cur.flags |= BasicDwarfBlock;
249 				break;
250 			case 8:	/* const add pc */
251 				a = (255 - opcodebase) / linerange * quantum;
252 				if(trace) werrstr(" const add pc + %d\n", a);
253 				cur.addr += a;
254 				break;
255 			case 9:	/* fixed advance pc */
256 				a = dwarfget2(&b);
257 				if(trace) werrstr(" fixed advance pc + %d\n", a);
258 				cur.addr += a;
259 				break;
260 			case 10:	/* set prologue end */
261 				if(trace) werrstr(" set prologue end\n");
262 				cur.flags |= PrologueEnd;
263 				break;
264 			case 11:	/* set epilogue begin */
265 				if(trace) werrstr(" set epilogue begin\n");
266 				cur.flags |= EpilogueBegin;
267 				break;
268 			case 12:	/* set isa */
269 				if(trace) werrstr(" set isa\n");
270 				cur.isa = dwarfget128(&b);
271 				break;
272 			default:	/* something new - skip it */
273 				if(trace) werrstr(" unknown %d\n", opcount[op]);
274 				for(i=0; i<opcount[op]; i++)
275 					dwarfget128(&b);
276 				break;
277 			}
278 		}
279 	}
280 	if(b.p == nil)
281 		goto bad;
282 
283 	/* finally!  the data we seek is in "emit" */
284 
285 	if(emit.file == 0){
286 		werrstr("invalid file index in mapping data");
287 		goto out;
288 	}
289 	if(line)
290 		*line = emit.line;
291 
292 	/* skip over first emit.file-2 guys */
293 	b.p = (uchar*)files;
294 	for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
295 		dwarfgetstring(&b);
296 		dwarfget128(&b);
297 		dwarfget128(&b);
298 		dwarfget128(&b);
299 	}
300 	if(b.p == nil){
301 		werrstr("problem parsing file data second time (cannot happen)");
302 		goto bad;
303 	}
304 	if(*b.p == 0){
305 		if(i >= nf){
306 			werrstr("bad file index in mapping data");
307 			goto bad;
308 		}
309 		b.p = f[i];
310 	}
311 	s = dwarfgetstring(&b);
312 	if(file)
313 		*file = s;
314 	i = dwarfget128(&b);		/* directory */
315 	x = dwarfget128(&b);
316 	if(mtime)
317 		*mtime = x;
318 	x = dwarfget128(&b);
319 	if(length)
320 		*length = x;
321 
322 	/* fetch dir name */
323 	if(cdir)
324 		*cdir = sym.attrs.compdir;
325 
326 	if(dir){
327 		*dir = nil;
328 		b.p = dirs;
329 		for (x = 1; b.p && *b.p; x++)
330 			if (x == i) {
331 				*dir = dwarfgetstring(&b);
332 				break;
333 			}
334 	}
335 
336 	*function = nil;
337 	lastline = 0;
338 #if 0
339 	if (dwarfenumunit(d, unit, &proc) >= 0) {
340 		dwarfnextsymat(d, &proc, 0);
341 		while (dwarfnextsymat(d, &proc, 1) == 1) {
342 			if (proc.attrs.tag == TagSubprogram &&
343 				proc.attrs.have.name &&
344 				proc.attrs.declfile == emit.file &&
345 				proc.attrs.declline <= *line &&
346 				proc.attrs.declline > lastline) {
347 				lastline = proc.attrs.declline;
348 				free(*function);
349 				*function = malloc(strlen(proc.attrs.name)+1);
350 				strcpy(*function, proc.attrs.name);
351 			}
352 		}
353 	}
354 #elif 1
355 	ulong lastaddr = 0;
356 	*function = NULL;
357 	for (i = 0; i < d->pe->nsymbols; i++) {
358 		if (d->pe->symtab[i].address > lastaddr &&
359 			d->pe->symtab[i].address <= pc - d->pe->imagebase &&
360 			d->pe->symtab[i].address < d->pe->imagesize) {
361 			lastaddr = d->pe->symtab[i].address;
362 			*function = d->pe->symtab[i].name;
363 		}
364 	}
365 #else
366 	// *sigh* we get unrelocated low_pc and high_pc because the dwarf symbols
367 	// are not 'loaded' in the PE sense.
368 	if (dwarflookupfn(d, unit, pc, &proc) >= 0) {
369 		*function = malloc(strlen(proc.attrs.name)+1);
370 		strcpy(*function, proc.attrs.name);
371 	}
372 #endif
373 
374 	/* free at last, free at last */
375 	free(f);
376 	return 0;
377 
378 bad:
379 	werrstr("corrupted line mapping for 0x%x", pc);
380 out:
381 	free(f);
382 	return -1;
383 }
384 
385