Blob


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 */
14 #include <u.h>
15 #include <libc.h>
16 #include <bio.h>
17 #include "elf.h"
18 #include "dwarf.h"
20 #define trace 0
22 enum
23 {
24 Isstmt = 1<<0,
25 BasicDwarfBlock = 1<<1,
26 EndSequence = 1<<2,
27 PrologueEnd = 1<<3,
28 EpilogueBegin = 1<<4
29 };
31 typedef struct State State;
32 struct State
33 {
34 ulong addr;
35 ulong file;
36 ulong line;
37 ulong column;
38 ulong flags;
39 ulong isa;
40 };
42 int
43 dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, ulong *line, ulong *mtime, ulong *length)
44 {
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;
49 DwarfBuf b;
50 DwarfSym sym;
51 State emit, cur, reset;
52 uchar **f, **newf;
54 f = nil;
56 if(dwarfaddrtounit(d, pc, &unit) < 0
57 || dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0)
58 return -1;
60 if(!sym.attrs.have.stmtlist){
61 werrstr("no line mapping information for 0x%lux", pc);
62 return -1;
63 }
64 off = sym.attrs.stmtlist;
65 if(off >= d->line.len){
66 fprint(2, "bad stmtlist\n");
67 goto bad;
68 }
70 if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist);
72 memset(&b, 0, sizeof b);
73 b.d = d;
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? */
78 len = dwarfget4(&b);
79 if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
80 fprint(2, "bad len\n");
81 goto bad;
82 }
84 b.ep = b.p+len;
85 vers = dwarfget2(&b);
86 if(vers != 2){
87 werrstr("bad dwarf version 0x%lux", vers);
88 return -1;
89 }
91 len = dwarfget4(&b);
92 if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
93 fprint(2, "another bad len\n");
94 goto bad;
95 }
96 prog = b.p+len;
98 quantum = dwarfget1(&b);
99 isstmt = dwarfget1(&b);
100 linebase = (schar)dwarfget1(&b);
101 linerange = (schar)dwarfget1(&b);
102 opcodebase = dwarfget1(&b);
104 opcount = b.p-1;
105 dwarfgetnref(&b, opcodebase-1);
106 if(b.p == nil){
107 fprint(2, "bad opcode chart\n");
108 goto bad;
111 /* just skip the files and dirs for now; we'll come back */
112 dirs = (char*)b.p;
113 while(b.p!=nil && *b.p!=0)
114 dwarfgetstring(&b);
115 dwarfget1(&b);
117 files = (char*)b.p;
118 while(b.p!=nil && *b.p!=0){
119 dwarfgetstring(&b);
120 dwarfget128(&b);
121 dwarfget128(&b);
122 dwarfget128(&b);
124 dwarfget1(&b);
126 /* move on to the program */
127 if(b.p == nil || b.p > prog){
128 fprint(2, "bad header\n");
129 goto bad;
131 b.p = prog;
133 reset.addr = 0;
134 reset.file = 1;
135 reset.line = 1;
136 reset.column = 0;
137 reset.flags = isstmt ? Isstmt : 0;
138 reset.isa = 0;
140 cur = reset;
141 emit = reset;
142 nf = 0;
143 start = 0;
144 if(trace) fprint(2, "program @ %lud ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
145 first = 1;
146 while(b.p != nil){
147 op = dwarfget1(&b);
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;
152 cur.line += l;
153 cur.addr += a * quantum;
154 if(trace) fprint(2, " +%d,%d\n", a, l);
155 emit:
156 if(first){
157 if(cur.addr > pc){
158 werrstr("found wrong line mapping 0x%lux for pc 0x%lux", cur.addr, pc);
159 goto out;
161 first = 0;
162 start = cur.addr;
164 if(cur.addr > pc)
165 break;
166 if(b.p == nil){
167 werrstr("buffer underflow in line mapping");
168 goto out;
170 emit = cur;
171 if(emit.flags & EndSequence){
172 werrstr("found wrong line mapping 0x%lux-0x%lux for pc 0x%lux", start, cur.addr, pc);
173 goto out;
175 cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
176 }else{
177 switch(op){
178 case 0: /* extended op code */
179 if(trace) fprint(2, " ext");
180 len = dwarfget128(&b);
181 end = b.p+len;
182 if(b.p == nil || end > b.ep || end < b.p || len < 1)
183 goto bad;
184 switch(dwarfget1(&b)){
185 case 1: /* end sequence */
186 if(trace) fprint(2, " end\n");
187 cur.flags |= EndSequence;
188 goto emit;
189 case 2: /* set address */
190 cur.addr = dwarfgetaddr(&b);
191 if(trace) fprint(2, " set pc 0x%lux\n", cur.addr);
192 break;
193 case 3: /* define file */
194 newf = realloc(f, (nf+1)*sizeof(f[0]));
195 if(newf == nil)
196 goto out;
197 f = newf;
198 f[nf++] = b.p;
199 s = dwarfgetstring(&b);
200 dwarfget128(&b);
201 dwarfget128(&b);
202 dwarfget128(&b);
203 if(trace) fprint(2, " def file %s\n", s);
204 break;
206 if(b.p == nil || b.p > end)
207 goto bad;
208 b.p = end;
209 break;
210 case 1: /* emit */
211 if(trace) fprint(2, " emit\n");
212 goto emit;
213 case 2: /* advance pc */
214 a = dwarfget128(&b);
215 if(trace) fprint(2, " advance pc + %lud\n", a*quantum);
216 cur.addr += a * quantum;
217 break;
218 case 3: /* advance line */
219 l = dwarfget128s(&b);
220 if(trace) fprint(2, " advance line + %ld\n", l);
221 cur.line += l;
222 break;
223 case 4: /* set file */
224 if(trace) fprint(2, " set file\n");
225 cur.file = dwarfget128s(&b);
226 break;
227 case 5: /* set column */
228 if(trace) fprint(2, " set column\n");
229 cur.column = dwarfget128(&b);
230 break;
231 case 6: /* negate stmt */
232 if(trace) fprint(2, " negate stmt\n");
233 cur.flags ^= Isstmt;
234 break;
235 case 7: /* set basic block */
236 if(trace) fprint(2, " set basic block\n");
237 cur.flags |= BasicDwarfBlock;
238 break;
239 case 8: /* const add pc */
240 a = (255 - opcodebase) / linerange * quantum;
241 if(trace) fprint(2, " const add pc + %d\n", a);
242 cur.addr += a;
243 break;
244 case 9: /* fixed advance pc */
245 a = dwarfget2(&b);
246 if(trace) fprint(2, " fixed advance pc + %d\n", a);
247 cur.addr += a;
248 break;
249 case 10: /* set prologue end */
250 if(trace) fprint(2, " set prologue end\n");
251 cur.flags |= PrologueEnd;
252 break;
253 case 11: /* set epilogue begin */
254 if(trace) fprint(2, " set epilogue begin\n");
255 cur.flags |= EpilogueBegin;
256 break;
257 case 12: /* set isa */
258 if(trace) fprint(2, " set isa\n");
259 cur.isa = dwarfget128(&b);
260 break;
261 default: /* something new - skip it */
262 if(trace) fprint(2, " unknown %d\n", opcount[op]);
263 for(i=0; i<opcount[op]; i++)
264 dwarfget128(&b);
265 break;
269 if(b.p == nil)
270 goto bad;
272 /* finally! the data we seek is in "emit" */
274 if(emit.file == 0){
275 werrstr("invalid file index in mapping data");
276 goto out;
278 if(line)
279 *line = emit.line;
281 /* skip over first emit.file-2 guys */
282 b.p = (uchar*)files;
283 for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
284 dwarfgetstring(&b);
285 dwarfget128(&b);
286 dwarfget128(&b);
287 dwarfget128(&b);
289 if(b.p == nil){
290 werrstr("problem parsing file data second time (cannot happen)");
291 goto bad;
293 if(*b.p == 0){
294 if(i >= nf){
295 werrstr("bad file index in mapping data");
296 goto bad;
298 b.p = f[i];
300 s = dwarfgetstring(&b);
301 if(file)
302 *file = s;
303 i = dwarfget128(&b); /* directory */
304 x = dwarfget128(&b);
305 if(mtime)
306 *mtime = x;
307 x = dwarfget128(&b);
308 if(length)
309 *length = x;
311 /* fetch dir name */
312 if(cdir)
313 *cdir = sym.attrs.compdir;
315 if(dir){
316 if(i == 0)
317 *dir = nil;
318 else{
319 b.p = (uchar*)dirs;
320 for(i--; i>0 && b.p!=nil && *b.p!=0; i--)
321 dwarfgetstring(&b);
322 if(b.p==nil || *b.p==0){
323 werrstr("bad directory reference in line mapping");
324 goto out; /* can only happen with bad dir index */
326 *dir = dwarfgetstring(&b);
330 /* free at last, free at last */
331 free(f);
332 return 0;
334 bad:
335 werrstr("corrupted line mapping for 0x%lux", pc);
336 out:
337 free(f);
338 return -1;