1204 lines
25 KiB
C
1204 lines
25 KiB
C
/* $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(¤t_state, rchain->udesc.B2.ecount);
|
|
}
|
|
break;
|
|
case B3:
|
|
if (regionoffset < rchain->udesc.B3.t) {
|
|
poprecord(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_state, &rchain[i], rdepth);
|
|
break;
|
|
|
|
case B1:
|
|
case B2:
|
|
case B3:
|
|
case B4:
|
|
modifyrecord(¤t_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(¤t_state);
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return ¤t_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);
|
|
}
|
|
|