Reactos
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
27enum
28{
29 Isstmt = 1<<0,
30 BasicDwarfBlock = 1<<1,
31 EndSequence = 1<<2,
32 PrologueEnd = 1<<3,
33 EpilogueBegin = 1<<4
34};
35
36typedef struct State State;
37struct State
38{
39 ulong addr;
40 ulong file;
41 ulong line;
42 ulong column;
43 ulong flags;
44 ulong isa;
45};
46
47int
48dwarfpctoline(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
378bad:
379 werrstr("corrupted line mapping for 0x%x", pc);
380out:
381 free(f);
382 return -1;
383}
384