plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

dwarfcfa.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 }