dwarfpc.c (7715B)
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 <u.h> 15 #include <libc.h> 16 #include <bio.h> 17 #include "elf.h" 18 #include "dwarf.h" 19 20 #define trace 0 21 22 enum 23 { 24 Isstmt = 1<<0, 25 BasicDwarfBlock = 1<<1, 26 EndSequence = 1<<2, 27 PrologueEnd = 1<<3, 28 EpilogueBegin = 1<<4 29 }; 30 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 }; 41 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; 53 54 f = nil; 55 56 if(dwarfaddrtounit(d, pc, &unit) < 0 57 || dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0) 58 return -1; 59 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 } 69 70 if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist); 71 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? */ 77 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 } 83 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 } 90 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; 97 98 quantum = dwarfget1(&b); 99 isstmt = dwarfget1(&b); 100 linebase = (schar)dwarfget1(&b); 101 linerange = (schar)dwarfget1(&b); 102 opcodebase = dwarfget1(&b); 103 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; 109 } 110 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); 116 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); 123 } 124 dwarfget1(&b); 125 126 /* move on to the program */ 127 if(b.p == nil || b.p > prog){ 128 fprint(2, "bad header\n"); 129 goto bad; 130 } 131 b.p = prog; 132 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; 139 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; 160 } 161 first = 0; 162 start = cur.addr; 163 } 164 if(cur.addr > pc) 165 break; 166 if(b.p == nil){ 167 werrstr("buffer underflow in line mapping"); 168 goto out; 169 } 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; 174 } 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; 205 } 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; 266 } 267 } 268 } 269 if(b.p == nil) 270 goto bad; 271 272 /* finally! the data we seek is in "emit" */ 273 274 if(emit.file == 0){ 275 werrstr("invalid file index in mapping data"); 276 goto out; 277 } 278 if(line) 279 *line = emit.line; 280 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); 288 } 289 if(b.p == nil){ 290 werrstr("problem parsing file data second time (cannot happen)"); 291 goto bad; 292 } 293 if(*b.p == 0){ 294 if(i >= nf){ 295 werrstr("bad file index in mapping data"); 296 goto bad; 297 } 298 b.p = f[i]; 299 } 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; 310 311 /* fetch dir name */ 312 if(cdir) 313 *cdir = sym.attrs.compdir; 314 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 */ 325 } 326 *dir = dwarfgetstring(&b); 327 } 328 } 329 330 /* free at last, free at last */ 331 free(f); 332 return 0; 333 334 bad: 335 werrstr("corrupted line mapping for 0x%lux", pc); 336 out: 337 free(f); 338 return -1; 339 }