URI:
       tdwarfcfa.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tdwarfcfa.c (8704B)
       ---
            1 /*
            2  * Dwarf call frame unwinding.
            3  *
            4  * The call frame unwinding values are encoded using a state machine
            5  * like the pc<->line mapping, but it's a different machine.
            6  * The expressions to generate the old values are similar in function to the
            7  * ``dwarf expressions'' used for locations in the code, but of course not
            8  * the same encoding.
            9  */
           10 #include <u.h>
           11 #include <libc.h>
           12 #include <bio.h>
           13 #include "elf.h"
           14 #include "dwarf.h"
           15 
           16 #define trace 0
           17 
           18 typedef struct State State;
           19 struct State
           20 {
           21         ulong loc;
           22         ulong endloc;
           23         ulong iquantum;
           24         ulong dquantum;
           25         char *augmentation;
           26         int version;
           27         ulong rareg;
           28         DwarfBuf init;
           29         DwarfExpr *cfa;
           30         DwarfExpr *ra;
           31         DwarfExpr *r;
           32         DwarfExpr *initr;
           33         int nr;
           34         DwarfExpr **stack;
           35         int nstack;
           36 };
           37 
           38 static int findfde(Dwarf*, ulong, State*, DwarfBuf*);
           39 static int dexec(DwarfBuf*, State*, int);
           40 
           41 int
           42 dwarfunwind(Dwarf *d, ulong pc, DwarfExpr *cfa, DwarfExpr *ra, DwarfExpr *r, int nr)
           43 {
           44         int i, ret;
           45         DwarfBuf fde, b;
           46         DwarfExpr *initr;
           47         State s;
           48 
           49         initr = mallocz(nr*sizeof(initr[0]), 1);
           50         if(initr == 0)
           51                 return -1;
           52 
           53         memset(&s, 0, sizeof s);
           54         s.loc = 0;
           55         s.cfa = cfa;
           56         s.ra = ra;
           57         s.r = r;
           58         s.nr = nr;
           59 
           60         if(findfde(d, pc, &s, &fde) < 0){
           61                 free(initr);
           62                 return -1;
           63         }
           64 
           65         memset(r, 0, nr*sizeof(r[0]));
           66         for(i=0; i<nr; i++)
           67                 r[i].type = RuleSame;
           68         if(trace) fprint(2, "s.init %p-%p, fde %p-%p\n", s.init.p, s.init.ep, fde.p, fde.ep);
           69         b = s.init;
           70         if(dexec(&b, &s, 0) < 0)
           71                 goto err;
           72 
           73         s.initr = initr;
           74         memmove(initr, r, nr*sizeof(initr[0]));
           75 
           76         if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
           77         while(s.loc < pc){
           78                 if(trace) fprint(2, "s.loc 0x%lux pc 0x%lux\n", s.loc, pc);
           79                 if(dexec(&fde, &s, 1) < 0)
           80                         goto err;
           81         }
           82         *ra = s.r[s.rareg];
           83 
           84         ret = 0;
           85         goto out;
           86 
           87 err:
           88         ret = -1;
           89 out:
           90         free(initr);
           91         for(i=0; i<s.nstack; i++)
           92                 free(s.stack[i]);
           93         free(s.stack);
           94         return ret;
           95 }
           96 
           97 /*
           98  * XXX This turns out to be much more expensive than the actual
           99  * running of the machine in dexec.  It probably makes sense to
          100  * cache the last 10 or so fde's we've found, since stack traces
          101  * will keep asking for the same info over and over.
          102  */
          103 static int
          104 findfde(Dwarf *d, ulong pc, State *s, DwarfBuf *fde)
          105 {
          106         static int nbad;
          107         char *aug;
          108         uchar *next;
          109         int i, vers;
          110         ulong len, id, base, size;
          111         DwarfBuf b;
          112 
          113         if(d->frame.data == nil){
          114                 werrstr("no frame debugging information");
          115                 return -1;
          116         }
          117         b.d = d;
          118         b.p = d->frame.data;
          119         b.ep = b.p + d->frame.len;
          120         b.addrsize = d->addrsize;
          121         if(b.addrsize == 0)
          122                 b.addrsize = 4;        /* where should i find this? */
          123 
          124         for(; b.p < b.ep; b.p = next){
          125                 if((i = (b.p - d->frame.data) % b.addrsize))
          126                         b.p += b.addrsize - i;
          127                 len = dwarfget4(&b);
          128                 if(len > b.ep-b.p){
          129                         werrstr("bad length in cie/fde header");
          130                         return -1;
          131                 }
          132                 next = b.p+len;
          133                 id = dwarfget4(&b);
          134                 if(id == 0xFFFFFFFF){        /* CIE */
          135                         vers = dwarfget1(&b);
          136                         if(vers != 1 && vers != 2 && vers != 3){
          137                                 if(++nbad == 1)
          138                                         fprint(2, "unknown cie version %d (wanted 1-3)\n", vers);
          139                                 continue;
          140                         }
          141                         aug = dwarfgetstring(&b);
          142                         if(aug && *aug){
          143                                 if(++nbad == 1)
          144                                         fprint(2, "unknown augmentation: %s\n", aug);
          145                                 continue;
          146                         }
          147                         s->iquantum = dwarfget128(&b);
          148                         s->dquantum = dwarfget128s(&b);
          149                         s->rareg = dwarfget128(&b);
          150                         if(s->rareg > s->nr){
          151                                 werrstr("return address is register %d but only have %d registers",
          152                                         s->rareg, s->nr);
          153                                 return -1;
          154                         }
          155                         s->init.p = b.p;
          156                         s->init.ep = next;
          157                 }else{        /* FDE */
          158                         base = dwarfgetaddr(&b);
          159                         size = dwarfgetaddr(&b);
          160                         fde->p = b.p;
          161                         fde->ep = next;
          162                         s->loc = base;
          163                         s->endloc = base+size;
          164                         if(base <= pc && pc < base+size)
          165                                 return 0;
          166                 }
          167         }
          168         werrstr("cannot find call frame information for pc 0x%lux", pc);
          169         return -1;
          170 
          171 }
          172 
          173 static int
          174 checkreg(State *s, long r)
          175 {
          176         if(r < 0 || r >= s->nr){
          177                 werrstr("bad register number 0x%lux", r);
          178                 return -1;
          179         }
          180         return 0;
          181 }
          182 
          183 static int
          184 dexec(DwarfBuf *b, State *s, int locstop)
          185 {
          186         int c;
          187         long arg1, arg2;
          188         DwarfExpr *e, **p;
          189 
          190         for(;;){
          191                 if(b->p == b->ep){
          192                         if(s->initr)
          193                                 s->loc = s->endloc;
          194                         return 0;
          195                 }
          196                 c = dwarfget1(b);
          197                 if(b->p == nil){
          198                         werrstr("ran out of instructions during cfa program");
          199                         if(trace) fprint(2, "%r\n");
          200                         return -1;
          201                 }
          202                 if(trace) fprint(2, "+ loc=0x%lux op 0x%ux ", s->loc, c);
          203                 switch(c>>6){
          204                 case 1:        /* advance location */
          205                         arg1 = c&0x3F;
          206                 advance:
          207                         if(trace) fprint(2, "loc += %ld\n", arg1*s->iquantum);
          208                         s->loc += arg1 * s->iquantum;
          209                         if(locstop)
          210                                 return 0;
          211                         continue;
          212 
          213                 case 2:        /* offset rule */
          214                         arg1 = c&0x3F;
          215                         arg2 = dwarfget128(b);
          216                 offset:
          217                         if(trace) fprint(2, "r%ld += %ld\n", arg1, arg2*s->dquantum);
          218                         if(checkreg(s, arg1) < 0)
          219                                 return -1;
          220                         s->r[arg1].type = RuleCfaOffset;
          221                         s->r[arg1].offset = arg2 * s->dquantum;
          222                         continue;
          223 
          224                 case 3:        /* restore initial setting */
          225                         arg1 = c&0x3F;
          226                 restore:
          227                         if(trace) fprint(2, "r%ld = init\n", arg1);
          228                         if(checkreg(s, arg1) < 0)
          229                                 return -1;
          230                         s->r[arg1] = s->initr[arg1];
          231                         continue;
          232                 }
          233 
          234                 switch(c){
          235                 case 0:        /* nop */
          236                         if(trace) fprint(2, "nop\n");
          237                         continue;
          238 
          239                 case 0x01:        /* set location */
          240                         s->loc = dwarfgetaddr(b);
          241                         if(trace) fprint(2, "loc = 0x%lux\n", s->loc);
          242                         if(locstop)
          243                                 return 0;
          244                         continue;
          245 
          246                 case 0x02:        /* advance loc1 */
          247                         arg1 = dwarfget1(b);
          248                         goto advance;
          249 
          250                 case 0x03:        /* advance loc2 */
          251                         arg1 = dwarfget2(b);
          252                         goto advance;
          253 
          254                 case 0x04:        /* advance loc4 */
          255                         arg1 = dwarfget4(b);
          256                         goto advance;
          257 
          258                 case 0x05:        /* offset extended */
          259                         arg1 = dwarfget128(b);
          260                         arg2 = dwarfget128(b);
          261                         goto offset;
          262 
          263                 case 0x06:        /* restore extended */
          264                         arg1 = dwarfget128(b);
          265                         goto restore;
          266 
          267                 case 0x07:        /* undefined */
          268                         arg1 = dwarfget128(b);
          269                         if(trace) fprint(2, "r%ld = undef\n", arg1);
          270                         if(checkreg(s, arg1) < 0)
          271                                 return -1;
          272                         s->r[arg1].type = RuleUndef;
          273                         continue;
          274 
          275                 case 0x08:        /* same value */
          276                         arg1 = dwarfget128(b);
          277                         if(trace) fprint(2, "r%ld = same\n", arg1);
          278                         if(checkreg(s, arg1) < 0)
          279                                 return -1;
          280                         s->r[arg1].type = RuleSame;
          281                         continue;
          282 
          283                 case 0x09:        /* register */
          284                         arg1 = dwarfget128(b);
          285                         arg2 = dwarfget128(b);
          286                         if(trace) fprint(2, "r%ld = r%ld\n", arg1, arg2);
          287                         if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0)
          288                                 return -1;
          289                         s->r[arg1].type = RuleRegister;
          290                         s->r[arg1].reg = arg2;
          291                         continue;
          292 
          293                 case 0x0A:        /* remember state */
          294                         e = malloc(s->nr*sizeof(e[0]));
          295                         if(trace) fprint(2, "push\n");
          296                         if(e == nil)
          297                                 return -1;
          298                         p = realloc(s->stack, (s->nstack+1)*sizeof(s->stack[0]));
          299                         if(p == nil){
          300                                 free(e);
          301                                 return -1;
          302                         }
          303                         s->stack[s->nstack++] = e;
          304                         memmove(e, s->r, s->nr*sizeof(e[0]));
          305                         continue;
          306 
          307                 case 0x0B:        /* restore state */
          308                         if(trace) fprint(2, "pop\n");
          309                         if(s->nstack == 0){
          310                                 werrstr("restore state underflow");
          311                                 return -1;
          312                         }
          313                         e = s->stack[s->nstack-1];
          314                         memmove(s->r, e, s->nr*sizeof(e[0]));
          315                         p = realloc(s->stack, (s->nstack-1)*sizeof(s->stack[0]));
          316                         if(p == nil)
          317                                 return -1;
          318                         free(e);
          319                         s->nstack--;
          320                         continue;
          321 
          322                 case 0x0C:        /* def cfa */
          323                         arg1 = dwarfget128(b);
          324                         arg2 = dwarfget128(b);
          325                 defcfa:
          326                         if(trace) fprint(2, "cfa %ld(r%ld)\n", arg2, arg1);
          327                         if(checkreg(s, arg1) < 0)
          328                                 return -1;
          329                         s->cfa->type = RuleRegOff;
          330                         s->cfa->reg = arg1;
          331                         s->cfa->offset = arg2;
          332                         continue;
          333 
          334                 case 0x0D:        /* def cfa register */
          335                         arg1 = dwarfget128(b);
          336                         if(trace) fprint(2, "cfa reg r%ld\n", arg1);
          337                         if(s->cfa->type != RuleRegOff){
          338                                 werrstr("change CFA register but CFA not in register+offset form");
          339                                 return -1;
          340                         }
          341                         if(checkreg(s, arg1) < 0)
          342                                 return -1;
          343                         s->cfa->reg = arg1;
          344                         continue;
          345 
          346                 case 0x0E:        /* def cfa offset */
          347                         arg1 = dwarfget128(b);
          348                 cfaoffset:
          349                         if(trace) fprint(2, "cfa off %ld\n", arg1);
          350                         if(s->cfa->type != RuleRegOff){
          351                                 werrstr("change CFA offset but CFA not in register+offset form");
          352                                 return -1;
          353                         }
          354                         s->cfa->offset = arg1;
          355                         continue;
          356 
          357                 case 0x0F:        /* def cfa expression */
          358                         if(trace) fprint(2, "cfa expr\n");
          359                         s->cfa->type = RuleLocation;
          360                         s->cfa->loc.len = dwarfget128(b);
          361                         s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len);
          362                         continue;
          363 
          364                 case 0x10:        /* def reg expression */
          365                         arg1 = dwarfget128(b);
          366                         if(trace) fprint(2, "reg expr r%ld\n", arg1);
          367                         if(checkreg(s, arg1) < 0)
          368                                 return -1;
          369                         s->r[arg1].type = RuleLocation;
          370                         s->r[arg1].loc.len = dwarfget128(b);
          371                         s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len);
          372                         continue;
          373 
          374                 case 0x11:        /* offset extended */
          375                         arg1 = dwarfget128(b);
          376                         arg2 = dwarfget128s(b);
          377                         goto offset;
          378 
          379                 case 0x12:        /* cfa sf */
          380                         arg1 = dwarfget128(b);
          381                         arg2 = dwarfget128s(b);
          382                         goto defcfa;
          383 
          384                 case 0x13:        /* cfa offset sf */
          385                         arg1 = dwarfget128s(b);
          386                         goto cfaoffset;
          387 
          388                 default:        /* unknown */
          389                         werrstr("unknown opcode 0x%ux in cfa program", c);
          390                         return -1;
          391                 }
          392         }
          393         /* not reached */
          394 }