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