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