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