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 #define NTOSAPI 15 #include <ntddk.h> 16 #include <reactos/rossym.h> 17 #include "rossympriv.h" 18 #include <ntimage.h> 19 20 #define NDEBUG 21 #include <debug.h> 22 23 #include "dwarf.h" 24 #include "pe.h" 25 26 #define trace 0 27 28 enum 29 { 30 Isstmt = 1<<0, 31 BasicDwarfBlock = 1<<1, 32 EndSequence = 1<<2, 33 PrologueEnd = 1<<3, 34 EpilogueBegin = 1<<4 35 }; 36 37 typedef struct State State; 38 struct State 39 { 40 ulong addr; 41 ulong file; 42 ulong line; 43 ulong column; 44 ulong flags; 45 ulong isa; 46 }; 47 48 int 49 dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, char **function, ulong *line, ulong *mtime, ulong *length) 50 { 51 uchar *prog, *opcount, *end, *dirs; 52 ulong off, unit, len, vers, x, start, lastline; 53 int i, first, firstline, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf; 54 char *files, *s; 55 DwarfBuf b; 56 DwarfSym sym; 57 State emit, cur, reset; 58 uchar **f, **newf; 59 60 f = nil; 61 62 if(dwarfaddrtounit(d, pc, &unit) < 0 63 || dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0) 64 return -1; 65 66 if(!sym.attrs.have.stmtlist){ 67 werrstr("no line mapping information for 0x%x", pc); 68 return -1; 69 } 70 off = sym.attrs.stmtlist; 71 if(off >= d->line.len){ 72 werrstr("bad stmtlist\n"); 73 goto bad; 74 } 75 76 if(trace) werrstr("unit 0x%x stmtlist 0x%x", unit, sym.attrs.stmtlist); 77 78 memset(&b, 0, sizeof b); 79 b.d = d; 80 b.p = d->line.data + off; 81 b.ep = b.p + d->line.len; 82 b.addrsize = sym.b.addrsize; /* should i get this from somewhere else? */ 83 84 len = dwarfget4(&b); 85 if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){ 86 werrstr("bad len\n"); 87 goto bad; 88 } 89 90 b.ep = b.p+len; 91 vers = dwarfget2(&b); 92 if(vers != 2){ 93 werrstr("bad dwarf version 0x%x", vers); 94 return -1; 95 } 96 97 len = dwarfget4(&b); 98 if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){ 99 werrstr("another bad len\n"); 100 goto bad; 101 } 102 prog = b.p+len; 103 104 quantum = dwarfget1(&b); 105 isstmt = dwarfget1(&b); 106 linebase = (schar)dwarfget1(&b); 107 linerange = (schar)dwarfget1(&b); 108 opcodebase = dwarfget1(&b); 109 110 opcount = b.p-1; 111 dwarfgetnref(&b, opcodebase-1); 112 if(b.p == nil){ 113 werrstr("bad opcode chart\n"); 114 goto bad; 115 } 116 117 /* just skip the files and dirs for now; we'll come back */ 118 dirs = b.p; 119 while (b.p && *b.p) 120 dwarfgetstring(&b); 121 dwarfget1(&b); 122 123 files = (char*)b.p; 124 while(b.p!=nil && *b.p!=0){ 125 dwarfgetstring(&b); 126 dwarfget128(&b); 127 dwarfget128(&b); 128 dwarfget128(&b); 129 } 130 dwarfget1(&b); 131 132 /* move on to the program */ 133 if(b.p == nil || b.p > prog){ 134 werrstr("bad header\n"); 135 goto bad; 136 } 137 b.p = prog; 138 139 reset.addr = 0; 140 reset.file = 1; 141 reset.line = 1; 142 reset.column = 0; 143 reset.flags = isstmt ? Isstmt : 0; 144 reset.isa = 0; 145 146 cur = reset; 147 emit = reset; 148 nf = 0; 149 start = 0; 150 if(trace) werrstr("program @ %lu ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase); 151 first = 1; 152 while(b.p != nil){ 153 firstline = 0; 154 op = dwarfget1(&b); 155 if(trace) werrstr("\tline %lu, addr 0x%x, op %d %.10H", cur.line, cur.addr, op, b.p); 156 if(op >= opcodebase){ 157 a = (op - opcodebase) / linerange; 158 l = (op - opcodebase) % linerange + linebase; 159 cur.line += l; 160 cur.addr += a * quantum; 161 if(trace) werrstr(" +%d,%d\n", a, l); 162 emit: 163 if(first){ 164 if(cur.addr > pc){ 165 werrstr("found wrong line mapping 0x%x for pc 0x%x", cur.addr, pc); 166 /* This is an overzealous check. gcc can produce discontiguous ranges 167 and reorder statements, so it's possible for a future line to start 168 ahead of pc and still find a matching one. */ 169 /*goto out;*/ 170 firstline = 1; 171 } 172 first = 0; 173 start = cur.addr; 174 } 175 if(cur.addr > pc && !firstline) 176 break; 177 if(b.p == nil){ 178 werrstr("buffer underflow in line mapping"); 179 goto out; 180 } 181 emit = cur; 182 if(emit.flags & EndSequence){ 183 werrstr("found wrong line mapping 0x%x-0x%x for pc 0x%x", start, cur.addr, pc); 184 goto out; 185 } 186 cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin); 187 }else{ 188 switch(op){ 189 case 0: /* extended op code */ 190 if(trace) werrstr(" ext"); 191 len = dwarfget128(&b); 192 end = b.p+len; 193 if(b.p == nil || end > b.ep || end < b.p || len < 1) 194 goto bad; 195 switch(dwarfget1(&b)){ 196 case 1: /* end sequence */ 197 if(trace) werrstr(" end\n"); 198 cur.flags |= EndSequence; 199 goto emit; 200 case 2: /* set address */ 201 cur.addr = dwarfgetaddr(&b); 202 if(trace) werrstr(" set pc 0x%x\n", cur.addr); 203 break; 204 case 3: /* define file */ 205 newf = malloc(nf+1*sizeof(f[0])); 206 if (newf) 207 RtlMoveMemory(newf, f, nf*sizeof(f[0])); 208 if(newf == nil) 209 goto out; 210 f[nf++] = b.p; 211 s = dwarfgetstring(&b); 212 dwarfget128(&b); 213 dwarfget128(&b); 214 dwarfget128(&b); 215 if(trace) werrstr(" def file %s\n", s); 216 break; 217 } 218 if(b.p == nil || b.p > end) 219 goto bad; 220 b.p = end; 221 break; 222 case 1: /* emit */ 223 if(trace) werrstr(" emit\n"); 224 goto emit; 225 case 2: /* advance pc */ 226 a = dwarfget128(&b); 227 if(trace) werrstr(" advance pc + %lu\n", a*quantum); 228 cur.addr += a * quantum; 229 break; 230 case 3: /* advance line */ 231 l = dwarfget128s(&b); 232 if(trace) werrstr(" advance line + %ld\n", l); 233 cur.line += l; 234 break; 235 case 4: /* set file */ 236 if(trace) werrstr(" set file\n"); 237 cur.file = dwarfget128s(&b); 238 break; 239 case 5: /* set column */ 240 if(trace) werrstr(" set column\n"); 241 cur.column = dwarfget128(&b); 242 break; 243 case 6: /* negate stmt */ 244 if(trace) werrstr(" negate stmt\n"); 245 cur.flags ^= Isstmt; 246 break; 247 case 7: /* set basic block */ 248 if(trace) werrstr(" set basic block\n"); 249 cur.flags |= BasicDwarfBlock; 250 break; 251 case 8: /* const add pc */ 252 a = (255 - opcodebase) / linerange * quantum; 253 if(trace) werrstr(" const add pc + %d\n", a); 254 cur.addr += a; 255 break; 256 case 9: /* fixed advance pc */ 257 a = dwarfget2(&b); 258 if(trace) werrstr(" fixed advance pc + %d\n", a); 259 cur.addr += a; 260 break; 261 case 10: /* set prologue end */ 262 if(trace) werrstr(" set prologue end\n"); 263 cur.flags |= PrologueEnd; 264 break; 265 case 11: /* set epilogue begin */ 266 if(trace) werrstr(" set epilogue begin\n"); 267 cur.flags |= EpilogueBegin; 268 break; 269 case 12: /* set isa */ 270 if(trace) werrstr(" set isa\n"); 271 cur.isa = dwarfget128(&b); 272 break; 273 default: /* something new - skip it */ 274 if(trace) werrstr(" unknown %d\n", opcount[op]); 275 for(i=0; i<opcount[op]; i++) 276 dwarfget128(&b); 277 break; 278 } 279 } 280 } 281 if(b.p == nil) 282 goto bad; 283 284 /* finally! the data we seek is in "emit" */ 285 286 if(emit.file == 0){ 287 werrstr("invalid file index in mapping data"); 288 goto out; 289 } 290 if(line) 291 *line = emit.line; 292 293 /* skip over first emit.file-2 guys */ 294 b.p = (uchar*)files; 295 for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){ 296 dwarfgetstring(&b); 297 dwarfget128(&b); 298 dwarfget128(&b); 299 dwarfget128(&b); 300 } 301 if(b.p == nil){ 302 werrstr("problem parsing file data second time (cannot happen)"); 303 goto bad; 304 } 305 if(*b.p == 0){ 306 if(i >= nf){ 307 werrstr("bad file index in mapping data"); 308 goto bad; 309 } 310 b.p = f[i]; 311 } 312 s = dwarfgetstring(&b); 313 if(file) 314 *file = s; 315 i = dwarfget128(&b); /* directory */ 316 x = dwarfget128(&b); 317 if(mtime) 318 *mtime = x; 319 x = dwarfget128(&b); 320 if(length) 321 *length = x; 322 323 /* fetch dir name */ 324 if(cdir) 325 *cdir = sym.attrs.compdir; 326 327 if(dir){ 328 *dir = nil; 329 b.p = dirs; 330 for (x = 1; b.p && *b.p; x++) 331 if (x == i) { 332 *dir = dwarfgetstring(&b); 333 break; 334 } 335 } 336 337 *function = nil; 338 lastline = 0; 339 #if 0 340 if (dwarfenumunit(d, unit, &proc) >= 0) { 341 dwarfnextsymat(d, &proc, 0); 342 while (dwarfnextsymat(d, &proc, 1) == 1) { 343 if (proc.attrs.tag == TagSubprogram && 344 proc.attrs.have.name && 345 proc.attrs.declfile == emit.file && 346 proc.attrs.declline <= *line && 347 proc.attrs.declline > lastline) { 348 lastline = proc.attrs.declline; 349 free(*function); 350 *function = malloc(strlen(proc.attrs.name)+1); 351 strcpy(*function, proc.attrs.name); 352 } 353 } 354 } 355 #elif 1 356 ulong lastaddr = 0; 357 *function = NULL; 358 for (i = 0; i < d->pe->nsymbols; i++) { 359 if (d->pe->symtab[i].address > lastaddr && 360 d->pe->symtab[i].address <= pc - d->pe->imagebase && 361 d->pe->symtab[i].address < d->pe->imagesize) { 362 lastaddr = d->pe->symtab[i].address; 363 *function = d->pe->symtab[i].name; 364 } 365 } 366 #else 367 // *sigh* we get unrelocated low_pc and high_pc because the dwarf symbols 368 // are not 'loaded' in the PE sense. 369 if (dwarflookupfn(d, unit, pc, &proc) >= 0) { 370 *function = malloc(strlen(proc.attrs.name)+1); 371 strcpy(*function, proc.attrs.name); 372 } 373 #endif 374 375 /* free at last, free at last */ 376 free(f); 377 return 0; 378 379 bad: 380 werrstr("corrupted line mapping for 0x%x", pc); 381 out: 382 free(f); 383 return -1; 384 } 385 386