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