NetBSD-5.0.2/sys/arch/ia64/unwind/stackframe.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: stackframe.c,v 1.3 2007/02/28 04:21:51 thorpej Exp $	*/

/* Contributed to the NetBSD foundation by Cherry G. Mathew <cherry@mahiti.org>
 * This file contains routines to use decoded unwind descriptor entries
 * to build a stack configuration. The unwinder consults the stack 
 * configuration to fetch registers used to unwind the frame.
 * References:
 *	[1] section. 11.4.2.6., Itanium Software Conventions and 
 *			Runtime Architecture Guide.
 */

#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>


#include <ia64/unwind/decode.h>
#include <ia64/unwind/stackframe.h>

//#define UNWIND_DIAGNOSTIC

/* Global variables:
   array of struct recordchain
   size of record chain array.
*/
struct recordchain strc[MAXSTATERECS];
int rec_cnt = 0;

/* Build a recordchain of a region, given the pointer to unwind table
 * entry, and the number of entries to decode.
 */

void buildrecordchain(uint64_t unwind_infop, struct recordchain *xxx)
{


	uint64_t unwindstart, unwindend;
	uint64_t unwindlen;
	uint64_t region_len = 0;
	bool region_type = false; /* Prologue */

	struct unwind_hdr_t {
		uint64_t uwh;
	} *uwhp = (void *) unwind_infop;

	char *nextrecp, *recptr = (char *) unwind_infop + sizeof(uint64_t);
	
	unwindstart = (uint64_t) recptr;

	if (UNW_VER(uwhp->uwh) != 1) {
		printf("Wrong unwind version! \n");
		return;
	}
	
	unwindlen = UNW_LENGTH(uwhp->uwh) * sizeof(uint64_t);
	unwindend = unwindstart + unwindlen;

#ifdef UNWIND_DIAGNOSTIC
	printf("recptr = %p \n", recptr);
	printf("unwindlen = %lx \n", unwindlen);
	printf("unwindend = %lx \n", unwindend);
#endif

	/* XXX: Ignore zero length records. */


	for(rec_cnt = 0; rec_cnt < MAXSTATERECS && (uint64_t)recptr < unwindend;
	    rec_cnt++) {
		if ((nextrecp = unwind_decode_R1(recptr, &strc[rec_cnt].udesc))){
			region_len = strc[rec_cnt].udesc.R1.rlen;
			region_type = strc[rec_cnt].udesc.R1.r;
			strc[rec_cnt].type = R1;
			recptr = nextrecp;
			continue;
		}

		if ((nextrecp = unwind_decode_R2(recptr, &strc[rec_cnt].udesc))){
			region_len = strc[rec_cnt].udesc.R2.rlen;
			region_type = false; /* R2 regions are prologue regions */
			strc[rec_cnt].type = R2;
			recptr = nextrecp;
			continue;
		}

		if ((nextrecp = unwind_decode_R3(recptr, &strc[rec_cnt].udesc))){
			region_len = strc[rec_cnt].udesc.R3.rlen;
			region_type = strc[rec_cnt].udesc.R3.r;
			strc[rec_cnt].type = R3;
			recptr = nextrecp;
			continue;
		}

		if(region_type == false) { /* Prologue Region */
			if ((nextrecp = unwind_decode_P1(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P1;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_P2(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P2;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_P3(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P3;
				recptr = nextrecp;
				continue;
			}


			if ((nextrecp = unwind_decode_P4(recptr, &strc[rec_cnt].udesc, region_len))){
				strc[rec_cnt].type = P4;
				recptr = nextrecp;
				break;
			}
		

			if ((nextrecp = unwind_decode_P5(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P5;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_P6(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P6;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_P7(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P7;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_P8(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P8;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_P9(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P9;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_P10(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = P10;
				recptr = nextrecp;
				continue;
			}

			printf("Skipping prologue desc slot :: %d \n", rec_cnt);
		}

		else {
			
			if ((nextrecp = unwind_decode_B1(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = B1;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_B2(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = B2;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_B3(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = B3;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_B4(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = B4;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_X1(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = X1;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_X2(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = X2;
				recptr = nextrecp;
				continue;
			}


			if ((nextrecp = unwind_decode_X3(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = X3;
				recptr = nextrecp;
				continue;
			}

			if ((nextrecp = unwind_decode_X4(recptr, &strc[rec_cnt].udesc))){
				strc[rec_cnt].type = X4;
				recptr = nextrecp;
				continue;
			}

			printf("Skipping body desc slot :: %d \n", rec_cnt);

			
		}
	}

#ifdef UNWIND_DIAGNOSTIC
	int i;
	for(i = 0;i < rec_cnt;i++) {
		dump_recordchain(&strc[i]);
	}

#endif /* UNWIND_DIAGNOSTIC */

}




/* Debug support: dump a record chain entry */
void dump_recordchain(struct recordchain *rchain)
{

	switch(rchain->type) {
	case R1:


		printf("\t R1:");
		if(rchain->udesc.R1.r)
			printf("body (");
		else
			printf("prologue (");
		printf("rlen = %ld) \n", rchain->udesc.R1.rlen);
		break;

	case R2:
		printf("\t R2:");
		printf("prologue_gr (");
		printf("mask = %x, ", rchain->udesc.R2.mask);
		printf("grsave = %d, ", rchain->udesc.R2.grsave);
		printf("rlen = %ld )\n", rchain->udesc.R2.rlen);
		break;

	case R3:
		printf("\t R3:");
		if(rchain->udesc.R3.r)
			printf("body (");
		else
			printf("prologue (");
		printf("rlen = %ld )\n", rchain->udesc.R3.rlen);
		break;

	case P1:
		printf("\t\tP1:");
		printf("br_mem (brmask = %x) \n", rchain->udesc.P1.brmask);
		break;

	case P2:
		printf("\t\tP2:");
		printf("br_gr(brmask = %x, ", rchain->udesc.P2.brmask);
		printf("gr = %d ) \n", rchain->udesc.P2.gr);
		break;

	case P3:
		printf("\t\tP3:");
		switch(rchain->udesc.P3.r) {
		case 0:
			printf("psp_gr");
			break;
		case 1:
			printf("rp_gr");
			break;
		case 2:
			printf("pfs_gr");
			break;
		case 3:
			printf("preds_gr");
			break;
		case 4:
			printf("unat_gr");
			break;
		case 5:
			printf("lc_gr");
			break;
		case 6:
			printf("rp_br");
			break;
		case 7:
			printf("rnat_gr");
			break;
		case 8:
			printf("bsp_gr");
			break;
		case 9:
			printf("bspstore_gr");
			break;
		case 10:
			printf("fpsr_gr");
			break;
		case 11:
			printf("priunat_gr");
			break;
		default:
			printf("unknown desc: %d", rchain->udesc.P3.r);
				
		}
		printf("(gr/br = %d) \n", rchain->udesc.P3.grbr);

		break;

	case P4:
		printf("P4: (unimplemented): \n");
		break;

	case P5:
		printf("\t\tP5:");
		printf("frgr_mem(grmask = %x, frmask = %x )\n", 
		       rchain->udesc.P5.grmask, rchain->udesc.P5.frmask);
		break;

	case P6:
		printf("\t\tP6: ");
		if(rchain->udesc.P6.r)
			printf("gr_mem( ");
		else
			printf("fr_mem( ");
		printf("rmask = %x) \n", rchain->udesc.P6.rmask);
		break;

	case P7:
		printf("\t\tP7:");
		switch(rchain->udesc.P7.r) {
		case 0:
			printf("memstack_f( ");
			printf("t = %ld, ", rchain->udesc.P7.t);
			printf("size = %ld) \n", rchain->udesc.P7.size);
			break;
		case 1:
			printf("memstack_v( ");
			printf("t = %ld) \n", rchain->udesc.P7.t);
			break;
		case 2:
			printf("spillbase( ");
			printf("pspoff = %ld) \n", rchain->udesc.P7.t);
			break;
		case 3:
			printf("psp_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P7.t);
			break;
		case 4:
			printf("rp_when( ");
			printf("t = %ld) \n", rchain->udesc.P7.t);
			break;
		case 5:
			printf("rp_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P7.t);
			break;
		case 6:
			printf("pfs_when( ");
			printf("t = %ld) \n", rchain->udesc.P7.t);
			break;
		case 7:
			printf("pfs_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P7.t);
			break;
		case 8:
			printf("preds_when( ");
			printf("t = %ld) \n", rchain->udesc.P7.t);
			break;
		case 9:
			printf("preds_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P7.t);
			break;
		case 10:
			printf("lc_when( ");
			printf("t = %ld) \n", rchain->udesc.P7.t);
			break;
		case 11:
			printf("lc_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P7.t);
			break;
		case 12:
			printf("unat_when( ");
			printf("t = %ld) \n", rchain->udesc.P7.t);
			break;
		case 13:
			printf("unat_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P7.t);
			break;
		case 14:
			printf("fpsr_when( ");
			printf("t = %ld) \n", rchain->udesc.P7.t);
			break;
		case 15:
			printf("fpsr_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P7.t);
			break;
		default:
			printf("unknown \n");
		}

		break;
			
	case P8:
		printf("\t\tP8:");
		switch(rchain->udesc.P8.r) {
		case 1:
			printf("rp_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 2:
			printf("pfs_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 3:
			printf("preds_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 4:
			printf("lc_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 5:
			printf("unat_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 6:
			printf("fpsr_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 7:
			printf("bsp_when( ");
			printf("t = %ld) \n", rchain->udesc.P8.t);
			break;
		case 8:
			printf("bsp_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 9:
			printf("bsp_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 10:
			printf("bspstore_when( ");
			printf("t = %ld) \n", rchain->udesc.P8.t);
			break;
		case 11:
			printf("bspstore_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 12:
			printf("bspstore_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 13:
			printf("rnat_when( ");
			printf("t = %ld) \n", rchain->udesc.P8.t);
			break;
		case 14:
			printf("rnat_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 15:
			printf("rnat_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 16:
			printf("priunat_when_gr( ");
			printf("t = %ld) \n", rchain->udesc.P8.t);
			break;
		case 17:
			printf("priunat_psprel( ");
			printf("pspoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 18:
			printf("priunat_sprel( ");
			printf("spoff = %ld) \n", rchain->udesc.P8.t);
			break;
		case 19:
			printf("priunat_when_mem( ");
			printf("t = %ld) \n", rchain->udesc.P8.t);
			break;

		default:
			printf("unknown \n");
		}

		break;

	case P9:
		printf("\t\tP9:");
		printf("(grmask = %x, gr = %d) \n", 
		       rchain->udesc.P9.grmask, rchain->udesc.P9.gr);
		break;

	case P10:
		printf("\t\tP10:");
		printf("(abi: ");
		switch(rchain->udesc.P10.abi) {
		case 0:
			printf("Unix SVR4) \n");
			break;
		case 1:
			printf("HP-UX) \n");
			break;
		default:
			printf("Other) \n");
		}
		break;

	case B1:
		printf("\t\tB1:");
		if(rchain->udesc.B1.r)
			printf("copy_state( ");
		else
			printf("label_state( ");
		printf("label = %d) \n", rchain->udesc.B1.label);

		break;

	case B2:
		printf("\t\tB2:");
		printf("(ecount = %d, t = %ld)\n",
		       rchain->udesc.B2.ecount, rchain->udesc.B2.t);

		break;

	case B3:
		printf("\t\tB3:");
		printf("(t = %ld, ecount = %ld) \n",
		       rchain->udesc.B3.t, rchain->udesc.B3.ecount);

		break;

	case B4:
		printf("\t\tB4:");
		if(rchain->udesc.B4.r)
			printf("copy_state( ");
		else
			printf("label_state( ");

		printf("label = %ld) \n", rchain->udesc.B4.label);

		break;


	case X1:
		printf("\tX1:\n ");
		break;

	case X2:
		printf("\tX2:\n");
		break;

	case X3:
		printf("\tX3:\n");
		break;

	case X4:
		printf("\tX4:\n");
		break;
	default:
		printf("\tunknow: \n");
	}

}

/* State record stuff..... based on section 11. and Appendix A. of the 
 *"Itanium Software Conventions and Runtime Architecture Guide"
 */


/* Global variables:
 * 1. Two arrays of staterecords: recordstack[], recordstackcopy[]
 *	XXX:	Since we don't use malloc, we have two arbitrary sized arrays
 *		providing guaranteed memory from the BSS. See the TODO file 
 *		for more details.
 * 2. Two head variables to hold the member index: unwind_rsp,unwind_rscp
 */

struct staterecord recordstack[MAXSTATERECS];
struct staterecord recordstackcopy[MAXSTATERECS];
struct staterecord current_state;
struct staterecord *unwind_rsp, *unwind_rscp;


uint64_t spill_base = 0;	/* Base of spill area in memory stack frame as a psp relative offset */

/* Initialises a staterecord from a given template,
 * with default values as described by the Runtime Spec.
 */

void
initrecord(struct staterecord *target)
{
	target->bsp.where = UNSAVED;
	target->bsp.when = 0;
	target->bsp.offset = INVALID;
	target->psp.where = UNSAVED;
	target->psp.when = 0;
	target->psp.offset = INVALID;
	target->rp.where = UNSAVED;
	target->rp.when = 0;	
	target->rp.offset = INVALID;
	target->pfs.where = UNSAVED;
	target->pfs.when = 0;
	target->pfs.offset = INVALID;
}


/* Modifies a staterecord structure by parsing 
 * a single record chain structure.
 * regionoffset is the offset within a (prologue) region 
 * where the stack unwinding began.
 */

void modifyrecord(struct staterecord *srec, struct recordchain *rchain, 
			 uint64_t regionoffset)
{


	uint64_t grno = 32; /* Default start save GR for prologue_save 
			     * GRs.
			     */



	switch (rchain->type) {
	
	case R2:
		/* R2, prologue_gr is the only region encoding 
		 * with register save info.
		 */

		grno = rchain->udesc.R2.grsave;

		if (rchain->udesc.R2.mask & R2MASKRP) {
			srec->rp.when = 0;
			srec->rp.where = GRREL;
			srec->rp.offset = grno++;
		}

		if (rchain->udesc.R2.mask & R2MASKPFS) {
			srec->pfs.when = 0;
			srec->pfs.where = GRREL;
			srec->pfs.offset = grno++;
		}

		if (rchain->udesc.R2.mask & R2MASKPSP) {
			srec->psp.when = 0;
			srec->psp.where = GRREL;
			srec->psp.offset = grno++;
		}
		break;

	case P3:
		switch (rchain->udesc.P3.r) {
		case 0: /* psp_gr */
			if (srec->psp.when < regionoffset) {
				srec->psp.where = GRREL;
				srec->psp.offset = rchain->udesc.P3.grbr;
			}
			break;

		case 1: /* rp_gr */
			if (srec->rp.when < regionoffset) {
				srec->rp.where = GRREL;
				srec->rp.offset = rchain->udesc.P3.grbr;
			}
			break;

		case 2: /* pfs_gr */
			if (srec->pfs.when < regionoffset) {
				srec->pfs.where = GRREL;
				srec->pfs.offset = rchain->udesc.P3.grbr;
			}
			break;

		}
		break;


	/* XXX: P4 spill_mask and P7: spill_base are for GRs, FRs, and BRs. 
	 * We're not particularly worried about those right now.
	 */

	case P7:
		switch (rchain->udesc.P7.r) {

		case 0: /* mem_stack_f */
			if (srec->psp.offset != INVALID) printf("!!!saw mem_stack_f more than once. \n");
			srec->psp.when = rchain->udesc.P7.t;
			if (srec->psp.when < regionoffset) {
				srec->psp.where = IMMED;
				srec->psp.offset = rchain->udesc.P7.size; /* spsz.offset is "overloaded" */
			}
			break;

		case 1: /* mem_stack_v */
			srec->psp.when = rchain->udesc.P7.t;
			break;

		case 2: /* spill_base */
			spill_base = rchain->udesc.P7.t;
			break;

		case 3: /* psp_sprel */
			if (srec->psp.when < regionoffset) {
				srec->psp.where = SPREL;
				srec->psp.offset = rchain->udesc.P7.t;
			}
			break;
			
		case 4: /* rp_when */
			srec->rp.when = rchain->udesc.P7.t;
			/* XXX: Need to set to prologue_gr(grno) for the orphan case 
			 *	ie; _gr/_psprel/_sprel not set and therefore default
			 *	to begin from the gr specified in prologue_gr.
			 */
			break;

		case 5: /* rp_psprel */
			if (srec->rp.when < regionoffset) {
				srec->rp.where = PSPREL;
				srec->rp.offset = rchain->udesc.P7.t;
			}
			break;

		case 6: /* pfs_when */
			srec->pfs.when = rchain->udesc.P7.t;
			/* XXX: Need to set to prologue_gr(grno) for the orphan case 
			 *	ie; _gr/_psprel/_sprel not set and therefore default
			 *	to begin from the gr specified in prologue_gr.
			 */
			break;

		case 7: /* pfs_psprel */
			if (srec->pfs.when < regionoffset) {
				srec->pfs.where = PSPREL;
				srec->pfs.offset = rchain->udesc.P7.t;
			}
			break;

		}
		break;

	case P8:
		switch (rchain->udesc.P8.r) {
		case 1: /* rp_sprel */
			if (srec->rp.when < regionoffset) {
				srec->rp.where = SPREL;
				srec->rp.offset = rchain->udesc.P8.t;
			}
			break;
		case 2: /* pfs_sprel */
			if (srec->pfs.when < regionoffset) {
				srec->pfs.where = SPREL;
				srec->pfs.offset = rchain->udesc.P8.t;

			}
			break;
		}
		break;

	case B1:

		rchain->udesc.B1.r ? switchrecordstack(0) :
			clonerecordstack(0);
		break;

	case B2:
		if (regionoffset < rchain->udesc.B2.t) {
		poprecord(&current_state, rchain->udesc.B2.ecount);
		}
		break;
	case B3:
		if (regionoffset < rchain->udesc.B3.t) {
		poprecord(&current_state, rchain->udesc.B3.ecount);
		}
		break;
	case B4:
		rchain->udesc.B4.r ? switchrecordstack(0) :
			clonerecordstack(0);
		break;

	case X1:
	case X2:
	case X3:
		/* XXX: Todo */
		break;


	case R1:
	case R3:
	case P1:
	case P2:
	case P4:
	case P5:
	case P6:
	case P9:
	case P10:
	default:
		/* Ignore. */
		printf("XXX: Ignored. \n");
	}


}

void dump_staterecord(struct staterecord *srec)
{
	printf("rp.where: ");
	switch(srec->rp.where) {
	case UNSAVED:
		printf("UNSAVED ");
		break;
	case BRREL:
		printf("BRREL ");
		break;
	case GRREL:
		printf("GRREL ");
		break;
	case SPREL:
		printf("SPREL ");
		break;
	case PSPREL:
		printf("PSPSREL ");
		break;
	default:
		printf("unknown ");
	}

	printf(", rp.when = %lu, ", srec->rp.when);
	printf("rp.offset = %lu \n", srec->rp.offset);


	printf("pfs.where: ");
	switch(srec->pfs.where) {
	case UNSAVED:
		printf("UNSAVED ");
		break;
	case BRREL:
		printf("BRREL ");
		break;
	case GRREL:
		printf("GRREL ");
		break;
	case SPREL:
		printf("SPREL ");
		break;
	case PSPREL:
		printf("PSPSREL ");
		break;
	default:
		printf("unknown ");
	}

	printf(", pfs.when = %lu, ", srec->pfs.when);
	printf("pfs.offset = %lu \n", srec->pfs.offset);


}


/* Push a state record on the record stack. */

void pushrecord(struct staterecord *srec)
{
	if(unwind_rsp >= recordstack + MAXSTATERECS) {
		printf("Push exceeded array size!!! \n");
		return;
	}

	memcpy(unwind_rsp, srec, sizeof(struct staterecord));
	unwind_rsp++;

}

/* Pop n state records off the record stack. */

void poprecord(struct staterecord *srec, int n)
{
	if(unwind_rsp == recordstack) {
		printf("Popped beyond end of Stack!!! \n");
		return;
	}
	unwind_rsp -= n;
	memcpy(srec, unwind_rsp, sizeof(struct staterecord));
#ifdef DEBUG
	bzero(unwind_rsp, sizeof(struct staterecord) * n);
#endif

}

/* Clone the whole record stack upto this one. */
void clonerecordstack(u_int label)
{
	memcpy(recordstackcopy, recordstack, 
	       (unwind_rsp - recordstack) * sizeof(struct staterecord));
	unwind_rscp = unwind_rsp;
}

/* Discard the current stack, and adopt a clone. */
void switchrecordstack(u_int label)
{
	memcpy((void *) recordstack, (void *) recordstackcopy, 
	       (unwind_rscp - recordstackcopy) * sizeof(struct staterecord));
	unwind_rsp = unwind_rscp;

}

/* In the context of a procedure:
 * Parses through a record chain, building, pushing and/or popping staterecords,
 * or cloning/destroying stacks of staterecords as required.
 * Parameters are:
 *	rchain: pointer to recordchain array.
 *	procoffset: offset of point of interest, in slots, within procedure starting from slot 0
 * This routine obeys [1]
 */
struct staterecord *buildrecordstack(struct recordchain *rchain, uint64_t procoffset)
{

	uint64_t rlen = 0;		/* Current region length, defaults to zero, if not specified */
	uint64_t roffset = 0;		/* Accumulated region length */
	uint64_t rdepth = 0;		/* Offset within current region */


	char *spill_mask = NULL;	/* Specifies when preserved registers are spilled, as a bit mask */

	spill_mask = NULL;
	bool rtype;

	unwind_rsp = recordstack; /* Start with bottom of staterecord stack. */

	initrecord(&current_state);

	int i;


	for (i = 0;i < rec_cnt;i++) {

		switch (rchain[i].type) {
		case R1:
			rlen = rchain[i].udesc.R1.rlen;
			rdepth = procoffset - roffset;
			if (rdepth < 0) goto out; /* Overshot Region containing procoffset. Bailout. */ 
			roffset += rlen;
			rtype = rchain[i].udesc.R1.r;
			if (!rtype) {
				pushrecord(&current_state);
			}
			break;

		case R3:
			rlen = rchain[i].udesc.R3.rlen;
			rdepth = procoffset - roffset;
			if (rdepth < 0) goto out; /* Overshot Region containing procoffset. Bailout. */ 
			roffset += rlen;
			rtype = rchain[i].udesc.R3.r;
			if (!rtype) {
				pushrecord(&current_state);
			}
			break;

		case R2:
			rlen = rchain[i].udesc.R2.rlen;
			rdepth = procoffset - roffset;
			if (rdepth < 0) goto out; /* Overshot Region containing procoffset. Bailout. */ 
			roffset += rlen;
			rtype = false; /* prologue region */
			pushrecord(&current_state);

			/* R2 has save info. Continue down. */

		case P1:
		case P2:
		case P3:
		case P4:
		case P5:
		case P6:
		case P7:
		case P8:
		case P9:
		case P10:
			modifyrecord(&current_state, &rchain[i], rdepth);
			break;

		case B1:
		case B2:
		case B3:
		case B4:
			modifyrecord(&current_state, &rchain[i], rlen - 1 - rdepth); 
			break;

		case X1:
		case X2:
		case X3:
		case X4:
		default:
			printf("Error: Unknown descriptor type!!! \n");

		}

#if UNWIND_DIAGNOSTIC
		dump_staterecord(&current_state);
#endif


	}

out:

	return &current_state;
}

void updateregs(struct unwind_frame *uwf, struct staterecord *srec, uint64_t procoffset) 
{

#ifdef UNWIND_DIAGNOSTIC
	printf("updateregs(): \n");
	printf("procoffset (slots) = %lu \n", procoffset);
#endif
	/* XXX: Update uwf for regs other than rp and pfs*/
	uint64_t roffset = 0;


	/* Uses shadow arrays to update uwf from srec in a loop. */
	/* Count of number of regstate elements in struct staterecord */
	int statecount = sizeof(struct staterecord)/sizeof(struct regstate);	
	/* Pointer to current regstate. */
	struct regstate *stptr = (void *) srec;				
	/* Pointer to current unwind_frame element */
	uint64_t *gr = (void *) uwf;


	int i;

	for(i = 0; i < statecount; i++) {
		switch (stptr[i].where) { 
		case IMMED: /* currently only mem_stack_f */
			if (stptr[i].when >= procoffset) break;
			uwf->psp -= (stptr[i].offset << 4); 
			break;

		case GRREL:
			if (stptr[i].when >= procoffset) break;
		
			roffset = stptr[i].offset;
			if (roffset == 0) {
				gr[i] = 0;
				break;
			}


			if (roffset < 32) {
				printf("GR%ld: static register save ??? \n", roffset);
				break;
			}

			/* Fetch from bsp + offset - 32 + Adjust for RNAT. */
			roffset -= 32;
			gr[i] = ia64_getrse_gr(uwf->bsp, roffset);
			break;

		case SPREL:
			if (stptr[i].when >= procoffset) break;

			/* Check if frame has been setup. */
			if (srec->psp.offset == INVALID) {
				printf("sprel used without setting up stackframe!!! \n");
				break;
			}

			roffset = stptr[i].offset;

			/* Fetch from sp + offset */
			memcpy(&gr[i], (char *) uwf->sp + roffset * 4, sizeof(uint64_t));
			break;


		case PSPREL:
			if (stptr[i].when >= procoffset) break;

			/* Check if frame has been setup. */
			if (srec->psp.offset == INVALID) {
				printf("psprel used without setting up stackframe!!! \n");
				break;
			}

			roffset = stptr[i].offset;

			/* Fetch from sp + offset */
			memcpy(&gr[i], (char *) uwf->psp + 16 - (roffset * 4), sizeof(uint64_t));
			break;

		case UNSAVED:
		case BRREL:
		default:
#ifdef UNWIND_DIAGNOSTIC
			printf ("updateregs: reg[%d] is UNSAVED \n", i);
#endif
			break;
			/* XXX: Not implemented yet. */
		}

	}		

}


/* Locates unwind table entry, given unwind table entry info.
 * Expects the variables ia64_unwindtab, and ia64_unwindtablen
 * to be set appropriately.
 */

struct uwtable_ent *
get_unwind_table_entry(uint64_t iprel)
{

	extern uint64_t ia64_unwindtab, ia64_unwindtablen;

	struct uwtable_ent *uwt;


	int tabent;

	for(uwt = (struct uwtable_ent *) ia64_unwindtab, tabent = 0; 
	    /* The Runtime spec tells me the table entries are sorted. */
	    uwt->end <= iprel && tabent < ia64_unwindtablen;
	    uwt++, tabent += sizeof(struct uwtable_ent));


	if (!(uwt->start <= iprel && iprel < uwt->end)) {
#ifdef UNWIND_DIAGNOSTIC
		printf("Entry not found \n");
		printf("iprel = %lx \n", iprel);
		printf("uwt->start = %lx \nuwt->end = %lx \n",
		       uwt->start, uwt->end);
		printf("tabent = %d \n", tabent);
		printf("ia64_unwindtablen = %ld \n", 
		       ia64_unwindtablen);
#endif
		return NULL;
	}

#ifdef UNWIND_DIAGNOSTIC
	printf("uwt->start = %lx \nuwt->end = %lx \n"
	       "uwt->infoptr = %p\n", uwt->start, uwt->end, uwt->infoptr);
#endif

	return uwt;
}


/* 
 * Reads unwind table info and updates register values.
 */

void 
patchunwindframe(struct unwind_frame *uwf, uint64_t iprel, uint64_t relocoffset) 
{

	extern struct recordchain strc[];
	struct staterecord *srec;
	struct uwtable_ent *uwt;
	uint64_t infoptr, procoffset, slotoffset;

	if (iprel < 0) {
		panic("unwind ip out of range!!! \n");
		return;
	}


	uwt = get_unwind_table_entry(iprel);

	if (uwt == NULL) return;

	infoptr = (uint64_t) uwt->infoptr + relocoffset;

	if (infoptr > relocoffset) {
		buildrecordchain(infoptr, NULL);
	}
	else return;
	
	slotoffset = iprel & 3;

	/* procoffset in Number of _slots_ , _not_ a byte offset. */

	procoffset = (((iprel - slotoffset) - (uwt->start)) / 0x10 * 3) + slotoffset; 
	srec = buildrecordstack(strc, procoffset);

	updateregs(uwf, srec, procoffset);
}