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
dwarfpctoline(Dwarf * d,DwarfSym * proc,ulong pc,char ** file,char ** function,ulong * line)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
RosSymFreeInfo(PROSSYM_LINEINFO LineInfo)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