2 * Dwarf pc to source line conversion.
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:
7 * long line2addr(ulong line, ulong basepc)
9 * which returns the smallest pc > basepc with line number line (ignoring file name).
11 * The encoding may be small, but it sure isn't simple!
25 BasicDwarfBlock = 1<<1,
31 typedef struct State State;
43 dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, ulong *line, ulong *mtime, ulong *length)
45 uchar *prog, *opcount, *end;
46 ulong off, unit, len, vers, x, start;
47 int i, first, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf;
48 char *files, *dirs, *s;
51 State emit, cur, reset;
56 if(dwarfaddrtounit(d, pc, &unit) < 0
57 || dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0)
60 if(!sym.attrs.have.stmtlist){
61 werrstr("no line mapping information for 0x%lux", pc);
64 off = sym.attrs.stmtlist;
65 if(off >= d->line.len){
66 fprint(2, "bad stmtlist\n");
70 if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist);
72 memset(&b, 0, sizeof b);
74 b.p = d->line.data + off;
75 b.ep = b.p + d->line.len;
76 b.addrsize = sym.b.addrsize; /* should i get this from somewhere else? */
79 if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
80 fprint(2, "bad len\n");
87 werrstr("bad dwarf version 0x%lux", vers);
92 if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
93 fprint(2, "another bad len\n");
98 quantum = dwarfget1(&b);
99 isstmt = dwarfget1(&b);
100 linebase = (schar)dwarfget1(&b);
101 linerange = (schar)dwarfget1(&b);
102 opcodebase = dwarfget1(&b);
105 dwarfgetnref(&b, opcodebase-1);
107 fprint(2, "bad opcode chart\n");
111 /* just skip the files and dirs for now; we'll come back */
113 while(b.p!=nil && *b.p!=0)
118 while(b.p!=nil && *b.p!=0){
126 /* move on to the program */
127 if(b.p == nil || b.p > prog){
128 fprint(2, "bad header\n");
137 reset.flags = isstmt ? Isstmt : 0;
144 if(trace) fprint(2, "program @ %lud ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
148 if(trace) fprint(2, "\tline %lud, addr 0x%lux, op %d %.10H", cur.line, cur.addr, op, b.p);
149 if(op >= opcodebase){
150 a = (op - opcodebase) / linerange;
151 l = (op - opcodebase) % linerange + linebase;
153 cur.addr += a * quantum;
154 if(trace) fprint(2, " +%d,%d\n", a, l);
158 werrstr("found wrong line mapping 0x%lux for pc 0x%lux", cur.addr, pc);
167 werrstr("buffer underflow in line mapping");
171 if(emit.flags & EndSequence){
172 werrstr("found wrong line mapping 0x%lux-0x%lux for pc 0x%lux", start, cur.addr, pc);
175 cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
178 case 0: /* extended op code */
179 if(trace) fprint(2, " ext");
180 len = dwarfget128(&b);
182 if(b.p == nil || end > b.ep || end < b.p || len < 1)
184 switch(dwarfget1(&b)){
185 case 1: /* end sequence */
186 if(trace) fprint(2, " end\n");
187 cur.flags |= EndSequence;
189 case 2: /* set address */
190 cur.addr = dwarfgetaddr(&b);
191 if(trace) fprint(2, " set pc 0x%lux\n", cur.addr);
193 case 3: /* define file */
194 newf = realloc(f, (nf+1)*sizeof(f[0]));
198 s = dwarfgetstring(&b);
202 if(trace) fprint(2, " def file %s\n", s);
205 if(b.p == nil || b.p > end)
210 if(trace) fprint(2, " emit\n");
212 case 2: /* advance pc */
214 if(trace) fprint(2, " advance pc + %lud\n", a*quantum);
215 cur.addr += a * quantum;
217 case 3: /* advance line */
218 l = dwarfget128s(&b);
219 if(trace) fprint(2, " advance line + %ld\n", l);
222 case 4: /* set file */
223 if(trace) fprint(2, " set file\n");
224 cur.file = dwarfget128s(&b);
226 case 5: /* set column */
227 if(trace) fprint(2, " set column\n");
228 cur.column = dwarfget128(&b);
230 case 6: /* negate stmt */
231 if(trace) fprint(2, " negate stmt\n");
234 case 7: /* set basic block */
235 if(trace) fprint(2, " set basic block\n");
236 cur.flags |= BasicDwarfBlock;
238 case 8: /* const add pc */
239 a = (255 - opcodebase) / linerange * quantum;
240 if(trace) fprint(2, " const add pc + %d\n", a);
243 case 9: /* fixed advance pc */
245 if(trace) fprint(2, " fixed advance pc + %d\n", a);
248 case 10: /* set prologue end */
249 if(trace) fprint(2, " set prologue end\n");
250 cur.flags |= PrologueEnd;
252 case 11: /* set epilogue begin */
253 if(trace) fprint(2, " set epilogue begin\n");
254 cur.flags |= EpilogueBegin;
256 case 12: /* set isa */
257 if(trace) fprint(2, " set isa\n");
258 cur.isa = dwarfget128(&b);
260 default: /* something new - skip it */
261 if(trace) fprint(2, " unknown %d\n", opcount[op]);
262 for(i=0; i<opcount[op]; i++)
271 /* finally! the data we seek is in "emit" */
274 werrstr("invalid file index in mapping data");
280 /* skip over first emit.file-2 guys */
282 for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
289 werrstr("problem parsing file data second time (cannot happen)");
294 werrstr("bad file index in mapping data");
299 s = dwarfgetstring(&b);
302 i = dwarfget128(&b); /* directory */
312 *cdir = sym.attrs.compdir;
319 for(i--; i>0 && b.p!=nil && *b.p!=0; i--)
321 if(b.p==nil || *b.p==0){
322 werrstr("bad directory reference in line mapping");
323 goto out; /* can only happen with bad dir index */
325 *dir = dwarfgetstring(&b);
329 /* free at last, free at last */
334 werrstr("corrupted line mapping for 0x%lux", pc);