tdwarfpc.c - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tdwarfpc.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 }