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