[CMAKE]
[reactos.git] / reactos / 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, 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