void fgncal_unwind(void) { mv_stent *mvc; assert((msp <= stackbase) && (msp > stacktop)); assert((mv_chain <= (mv_stent *)stackbase) && (mv_chain > (mv_stent *)stacktop)); assert((frame_pointer <= (stack_frame*)stackbase) && (frame_pointer > (stack_frame *)stacktop)); while (frame_pointer && (frame_pointer < (stack_frame *)fgncal_stack)) { # ifdef GTM_TRIGGER if (SFT_TRIGR & frame_pointer->type) gtm_trigger_fini(TRUE, FALSE); else # endif op_unwind(); } for (mvc = mv_chain; mvc < (mv_stent *)fgncal_stack; ) { unw_mv_ent(mvc); mvc = (mv_stent *)(mvc->mv_st_next + (char *) mvc); } mv_chain = mvc; msp = fgncal_stack; if (msp > stackbase) rts_error(VARLSTCNT(1) ERR_STACKUNDERFLO); }
/* this has to be maintained in parallel with unw_retarg(), the unwind with a return argument (extrinisic quit) routine */ void op_unwind(void) { mv_stent *mvc; error_def(ERR_STACKUNDERFLO); error_def(ERR_TPQUIT); if (tp_pointer && tp_pointer->fp <= frame_pointer) rts_error(VARLSTCNT(1) ERR_TPQUIT); /* Note that error_ret() should be invoked only after the rts_error() of TPQUIT. * This is so the TPQUIT error gets noted down in $ECODE (which will not happen if error_ret() is called before). */ if (!skip_error_ret) { INVOKE_ERROR_RET_IF_NEEDED; } else { if (NULL != error_frame) { assert(error_frame >= frame_pointer); if (error_frame <= frame_pointer) NULLIFY_ERROR_FRAME; /* ZGOTO to frame level lower than primary error level cancels error mode */ } skip_error_ret = FALSE; /* reset at the earliest point although caller (goframes()) does reset it just in * case an error occurs before we return to the caller */ } assert(msp <= stackbase && msp > stacktop); assert(mv_chain <= (mv_stent *)stackbase && mv_chain > (mv_stent *)stacktop); assert(frame_pointer <= (stack_frame*)stackbase && frame_pointer > (stack_frame *)stacktop); /* See if unwinding an indirect frame */ IF_INDR_FRAME_CLEANUP_CACHE_ENTRY(frame_pointer); for (mvc = mv_chain; mvc < (mv_stent *)frame_pointer; ) { unw_mv_ent(mvc); mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc); } if (is_tracing_on) (*unw_prof_frame_ptr)(); mv_chain = mvc; msp = (unsigned char *)frame_pointer + sizeof(stack_frame); if (msp > stackbase) rts_error(VARLSTCNT(1) ERR_STACKUNDERFLO); frame_pointer = frame_pointer->old_frame_pointer; if (NULL != zyerr_frame && frame_pointer > zyerr_frame) zyerr_frame = NULL; if (frame_pointer) { if (frame_pointer < (stack_frame *)msp || frame_pointer > (stack_frame *)stackbase || frame_pointer < (stack_frame *)stacktop) rts_error(VARLSTCNT(1) ERR_STACKUNDERFLO); } return; }
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the * very last level. * * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error. */ int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc) { ht_ent_mname *tabent; lv_val *inuse_lv; int elemindx; mv_stent *mvc; lvscan_blk *lvscan, *newlvscan; lvTree *lvt_child; boolean_t var_cloned; assert(curr_lv); assert(LV_IS_BASE_VAR(curr_lv)); assert(curr_lv->tp_var); DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr" save_lv: 0x"lvaddr"\n", restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv)); DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr " symval: 0x"lvaddr"/0x"lvaddr"\n", tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval)); /* First get the stack in the position where we can actually process this entry. Need to make sure we are processing * the symbol table we need to be processing so unwind enough stuff to get there. */ if (curr_symval != tp_pointer->sym) { /* Unwind as many stackframes as are necessary up to the max */ while((curr_symval != tp_pointer->sym) && (frame_pointer < tp_pointer->fp)) { # ifdef GTM_TRIGGER if (SFT_TRIGR & frame_pointer->type) { /* We have encountered a trigger base frame. We cannot unroll it because there are C frames * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so * it can unroll the base frame and rethrow the error to properly unroll the C stack. */ *tprestart_rc = ERR_TPRETRY; tprestart_state = TPRESTART_STATE_TPUNW; DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - " "rethrowing\n")); return -1; } # endif op_unwind(); } if (curr_symval != tp_pointer->sym) { /* Unwind as many mv_stents as are necessary up to the max */ mvc = mv_chain; while((curr_symval != tp_pointer->sym) && (mvc < tp_pointer->mvc)) { unw_mv_ent(mvc); mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc); } mv_chain = mvc; /* Final check */ assertpro(curr_symval == tp_pointer->sym); } } var_cloned = curr_lv->tp_var->var_cloned; if (var_cloned) { /* Var/tree has been copied (and modified) -- see about restoring it */ DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n")); if (NULL != restore_ent->key.var_name.addr) { /* Restore data into a named variable (hash table entry) * Step 1 -- find its hash table address to see what lv_val is there now. */ tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key); assert(tabent); /* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val * currently in use. Decrement its use count (which will delete it and the tree if * it is no longer used) and replace with desired previous lv_val whose use count * was incremented when it was saved. */ if (curr_lv != (inuse_lv = (lv_val *)tabent->value)) /* Note assignment */ { if (inuse_lv) DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE); DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n", tabent, tabent->value, curr_lv)); tabent->value = (void *)curr_lv; INCR_TREFCNT(curr_lv); /* Back in the hash table, bump its reference */ } } /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */ /* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value. * Get rid of its current tree if any. */ if (lvt_child = LV_GET_CHILD(curr_lv)) /* Note assignment */ { DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv)); assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child)); LV_CHILD(curr_lv) = NULL; /* prevent recursion due to alias containers */ lv_killarray(lvt_child, FALSE); } /* Step 4: Copy in the needed fields from the saved flavor lv_val back to curr_lv. * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date. */ DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n", save_lv, curr_lv)); /* The following is optimized to do the initialization of just the needed structure members. For that it assumes a * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will * automatically show an issue here and cause the below initialization to be accordingly reworked. */ assert(0 == OFFSETOF(lv_val, v)); assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs)); assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats)); assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont)); assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark)); assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var)); assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val)); /* save_lv -> curr_lv Copy begin */ curr_lv->v = save_lv->v; curr_lv->ptrs = save_lv->ptrs; assert(0 < curr_lv->stats.trefcnt); /* No need to copy "stats" as curr_lv is more uptodate */ assert(0 < curr_lv->stats.crefcnt); assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont))); curr_lv->has_aliascont = save_lv->has_aliascont; DBGALS_ONLY(curr_lv->lvmon_mark = save_lv->has_aliascont); assert(save_lv->tp_var == curr_lv->tp_var); /* no need to copy this field */ /* save_lv -> curr_lv Copy done */ /* Some fixup may need to be done if the variable was cloned (and thus moved around) */ curr_lv->tp_var->var_cloned = FALSE; if (lvt_child = LV_GET_CHILD(curr_lv)) { /* Some pointer fix up needs to be done since the owner of the restored tree changed */ assert(LVT_PARENT(lvt_child) == ((lvTreeNode *)curr_lv->tp_var->save_value)); LV_CHILD(save_lv) = NULL; /* now that curr_lv->tp_var->var_cloned has been reset */ LVT_PARENT(lvt_child) = (lvTreeNode *)curr_lv; if (curr_lv->has_aliascont && (NULL != lvscan_anchor)) { /* Some ref counts need to be restored for arrays this tree points to -- but only if the * array contains pointers (alias containers). */ DBGRFCT((stderr, "\ntp_unwind_restlv: Putting lv 0x:"lvaddr" on the lvscan list\n", curr_lv)); /* This array needs to have container pointer target reference counts reestablished. Record * the lv so this can happen after all vars are restored. */ lvscan = *lvscan_anchor; elemindx = lvscan->elemcnt++; /* Note post increment so elemindx has minus-one value */ if (ARY_SCNCNTNR_MAX < elemindx) { /* Present block is full so allocate a new one and chain it on */ lvscan->elemcnt--; /* New element ended up not being in that block.. */ newlvscan = (lvscan_blk *)malloc(SIZEOF(lvscan_blk)); newlvscan->next = lvscan; newlvscan->elemcnt = 1; /* Going to use first one *now* */ elemindx = 0; *lvscan_anchor = newlvscan; lvscan = newlvscan; } assert((ARY_SCNCNTNR_MAX >= elemindx) && (0 <= elemindx)); lvscan->ary_scncntnr[elemindx] = curr_lv; } } } else { DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was NOT modified or cloned\n")); assert(NULL == LV_CHILD(save_lv)); assert(!save_lv->tp_var->var_cloned); /* We know that the subscript array underneath curr_lv did not change since saving it into save_lv. But the * unsubscripted lv could have changed (have no way of checking if that is the case) so restore it unconditionally. */ curr_lv->v = save_lv->v; /* No need to copy "save_lv->ptrs" as "ptrs" contains 2 fields both of which are already correct in "curr_lv" */ assert(save_lv->ptrs.val_ent.parent.sym == curr_lv->ptrs.val_ent.parent.sym); assert(NULL == save_lv->ptrs.val_ent.children); /* No need to copy "save_lv->stats" as "curr_lv->stats" is more uptodate */ assert(save_lv->has_aliascont == curr_lv->has_aliascont); /* no need to copy this field */ assert(save_lv->lvmon_mark == curr_lv->lvmon_mark); /* no need to copy this field */ assert(save_lv->tp_var == curr_lv->tp_var); /* no need to copy this field */ } if (NULL == lvscan_anchor) /* Means this is completely unwinding a nested level so we need to reset the tstartcycle in this * lvval so it gets handled correctly when this lv is encountered again after the restart completes. */ curr_lv->stats.tstartcycle = 0; return 0; }
/* This has to be maintained in parallel with op_unwind(), the unwind without a return argument (intrinsic quit) routine. */ int unw_retarg(mval *src, boolean_t alias_return) { mval ret_value, *trg; boolean_t got_ret_target; stack_frame *prevfp; lv_val *srclv, *srclvc, *base_lv; symval *symlv, *symlvc; int4 srcsymvlvl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); assert(NULL == alias_retarg); alias_retarg = NULL; DBGEHND_ONLY(prevfp = frame_pointer); if (tp_pointer && tp_pointer->fp <= frame_pointer) rts_error(VARLSTCNT(1) ERR_TPQUIT); assert(msp <= stackbase && msp > stacktop); assert(mv_chain <= (mv_stent *)stackbase && mv_chain > (mv_stent *)stacktop); assert(frame_pointer <= (stack_frame *)stackbase && frame_pointer > (stack_frame *)stacktop); got_ret_target = FALSE; /* Before we do any unwinding or even verify the existence of the return var, check to see if we are returning * an alias (or container). We do this now because (1) alias returns don't need to be defined and (2) the returning * item could go out of scope in the unwinds so we have to bump the returned item's reference counts NOW. */ if (!alias_return) { /* Return of "regular" value - Verify it exists */ MV_FORCE_DEFINED(src); ret_value = *src; ret_value.mvtype &= ~MV_ALIASCONT; /* Make sure alias container of regular return does not propagate */ } else { /* QUIT *var or *var(indx..) syntax was used - see which one it was */ assert(NULL != src); srclv = (lv_val *)src; /* Since can never be an expression, this relationship is guaranteed */ if (!LV_IS_BASE_VAR(srclv)) { /* Have a potential container var - verify */ if (!(MV_ALIASCONT & srclv->v.mvtype)) rts_error(VARLSTCNT(1) ERR_ALIASEXPECTED); ret_value = *src; srclvc = (lv_val *)srclv->v.str.addr; assert(LV_IS_BASE_VAR(srclvc)); /* Verify base var */ assert(srclvc->stats.trefcnt >= srclvc->stats.crefcnt); assert(1 <= srclvc->stats.crefcnt); /* Verify is existing container ref */ base_lv = LV_GET_BASE_VAR(srclv); symlv = LV_GET_SYMVAL(base_lv); symlvc = LV_GET_SYMVAL(srclvc); MARK_ALIAS_ACTIVE(MIN(symlv->symvlvl, symlvc->symvlvl)); DBGRFCT((stderr, "unw_retarg: Returning alias container 0x"lvaddr" pointing to 0x"lvaddr" to caller\n", src, srclvc)); } else { /* Creating a new alias - create a container to pass back */ memcpy(&ret_value, &literal_null, SIZEOF(mval)); ret_value.mvtype |= MV_ALIASCONT; ret_value.str.addr = (char *)srclv; srclvc = srclv; MARK_ALIAS_ACTIVE(LV_SYMVAL(srclv)->symvlvl); DBGRFCT((stderr, "unw_retarg: Returning alias 0x"lvaddr" to caller\n", srclvc)); } INCR_TREFCNT(srclvc); INCR_CREFCNT(srclvc); /* This increment will be reversed if this container gets put into an alias */ /* We have a slight chicken-and-egg problem now. The mv_stent unwind loop below may pop a symbol table thus * destroying the lv_val in our container. To prevent this, we need to locate the parm block before the symval is * unwound and set the return value and alias_retarg appropriately so the symtab unwind logic called by * unw_mv_ent() can work any necessary relocation magic on the return var. */ trg = get_ret_targ(NULL); if (NULL != trg) { *trg = ret_value; alias_retarg = trg; got_ret_target = TRUE; } /* else fall into below which will raise the NOTEXTRINSIC error */ } /* Note: we are unwinding uncounted (indirect) frames here to allow the QUIT command to have indirect arguments * and thus be executed by commarg in an indirect frame. By unrolling the indirect frames here we get back to * the point where we can find where to put the quit value. */ unwind_nocounts(); assert(frame_pointer && (frame_pointer->type & SFT_COUNT)); while (mv_chain < (mv_stent *)frame_pointer) { msp = (unsigned char *)mv_chain; unw_mv_ent(mv_chain); POP_MV_STENT(); } if (0 <= frame_pointer->dollar_test) dollar_truth = (boolean_t)frame_pointer->dollar_test; /* Now that we have unwound the uncounted frames, we should be left with a counted frame that * contains some ret_value, NULL or not. If the value is non-NULL, let us restore the $TEST * value from that frame as well as update *trg for non-alias returns. */ if ((trg = frame_pointer->ret_value) && !alias_return) /* CAUTION: Assignment */ { /* If this is an alias_return arg, bypass the arg set logic which was done above. */ assert(!got_ret_target); got_ret_target = TRUE; *trg = ret_value; } /* do not throw an error if return value is expected from a non-extrinsic, but dollar_zquit_anyway is true */ if (!dollar_zquit_anyway && !got_ret_target) rts_error(VARLSTCNT(1) ERR_NOTEXTRINSIC); /* This routine was not invoked as an extrinsic function */ /* Note that error_ret() should be invoked only after the rts_error() of TPQUIT and NOTEXTRINSIC. * This is so the TPQUIT/NOTEXTRINSIC error gets noted down in $ECODE (which wont happen if error_ret() is called before). */ INVOKE_ERROR_RET_IF_NEEDED; if (is_tracing_on) (*unw_prof_frame_ptr)(); msp = (unsigned char *)frame_pointer + SIZEOF(stack_frame); DRAIN_GLVN_POOL_IF_NEEDED; PARM_ACT_UNSTACK_IF_NEEDED; frame_pointer = frame_pointer->old_frame_pointer; DBGEHND((stderr, "unw_retarg: Stack frame 0x"lvaddr" unwound - frame 0x"lvaddr" now current - New msp: 0x"lvaddr"\n", prevfp, frame_pointer, msp)); if ((NULL != zyerr_frame) && (frame_pointer > zyerr_frame)) zyerr_frame = NULL; if (!frame_pointer) rts_error(VARLSTCNT(1) ERR_STACKUNDERFLO); assert(frame_pointer >= (stack_frame *)msp); /* ensuring that trg is not NULL */ if (!dollar_zquit_anyway || trg) trg->mvtype |= MV_RETARG; assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); return 0; }
void flush_jmp (rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr) { mv_stent *mv_st_ent, *mv_st_prev; char *top; unsigned char *msp_save; int4 shift, size, mv_st_type; rhdtyp *old_rtnhdr; unwind_nocounts(); /* We are going to mutate the current frame from the program it was running to the program we want it to run. * If the current frame is marked for indr cache cleanup, do that cleanup now and unmark the frame. */ IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer); DBGEHND((stderr, "flush_jmp: Retargetting stack frame 0x"lvaddr" for transfer address 0x"lvaddr"\n", frame_pointer, transfer_addr)); /* Also unmark the SFF_ETRAP_ERR bit in case it is set. This way we ensure control gets transferred to * the mpc below instead of "error_return" (which is what getframe will do in case the bit is set). * It is ok to clear this bit because the global variable "error_frame" will still be set to point to * this frame so whenever we unwind out of this, we will rethrow the error at the parent frame. */ assert(!(frame_pointer->flags & SFF_ETRAP_ERR) || (NULL == error_frame) || (error_frame == frame_pointer)); assert(!(SFT_TRIGR & frame_pointer->type)); frame_pointer->flags &= SFF_ETRAP_ERR_OFF; /* clear SFF_ETRAP_ERR bit */ frame_pointer->flags &= SFF_IMPLTSTART_CALLD_OFF; /* clear SFF_IMPLTSTART_CALLD bit since this frame is being rewritten */ GTMTRIG_ONLY(DBGTRIGR((stderr, "flush_jmp: Turrning off SFF_IMPLTSTART_CALLD_OFF in frame 0x"lvaddr"\n", frame_pointer))); old_rtnhdr = frame_pointer->rvector; frame_pointer->rvector = rtn_base; /* Now that fp->rvector has been overwritten to new routine, check if the older routine had a "rtn_relinked" flag set * and if so that cleanup can be performed now. */ USHBIN_ONLY(CLEANUP_COPIED_RECURSIVE_RTN(old_rtnhdr)); /* cleanup if needed */ frame_pointer->vartab_ptr = (char *)VARTAB_ADR(rtn_base); frame_pointer->vartab_len = frame_pointer->rvector->vartab_len; frame_pointer->mpc = transfer_addr; frame_pointer->ctxt = context; #ifdef HAS_LITERAL_SECT frame_pointer->literal_ptr = (int4 *)LITERAL_ADR(rtn_base); #endif frame_pointer->temp_mvals = frame_pointer->rvector->temp_mvals; size = rtn_base->temp_size; frame_pointer->temps_ptr = (unsigned char *)frame_pointer - size; size += rtn_base->vartab_len * SIZEOF(ht_ent_mname *); frame_pointer->l_symtab = (ht_ent_mname **)((char *)frame_pointer - size); assert(frame_pointer->type & SFT_COUNT); assert((unsigned char *)mv_chain > stacktop && (unsigned char *)mv_chain <= stackbase); while (((char *)mv_chain < (char *)frame_pointer) && !mvs_save[mv_chain->mv_st_type]) { assert(MVST_TRIGR != mv_chain->mv_st_type); /* Should never unwind a trigger frame here */ msp = (unsigned char *)mv_chain; op_oldvar(); } if ((char *)mv_chain > (char *)frame_pointer) { msp_save = msp; msp = (unsigned char *)frame_pointer->l_symtab; if (msp <= stackwarn) { if (msp <= stacktop) { msp = msp_save; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKOFLOW); } else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKCRIT); } memset(msp, 0, size); DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr" New msp: 0x"lvaddr"\n", msp_save, msp)); return; } /* We kept one or more mv_stents for this frame. We may need to shift the stack to get room to create an l_symtab * for this re-purposed frame. In the above loop, we stopped searching the mv_stent chain at the first mv_stent we * knew we had to keep. Since we are moving things around anyway, see if there are any mv_stents associated * with this frame which don't need to be kept and can reclaim. */ mv_st_ent = mv_chain; mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next); top = (char *)mv_st_ent + mvs_size[mv_st_ent->mv_st_type]; while ((char *)mv_st_prev < (char *)frame_pointer) { mv_st_type = mv_st_prev->mv_st_type; assert(MVST_TRIGR != mv_st_type); /* Should never unwind a trigger frame here */ if (!mvs_save[mv_st_type]) { /* Don't need to keep this mv_stent. Remove it from the chain */ DBGEHND((stderr, "flush_jmp: Removing no-save mv_stent addr 0x"lvaddr" and type %d\n", mv_st_prev, mv_st_type)); unw_mv_ent(mv_st_prev); mv_st_ent->mv_st_next += mv_st_prev->mv_st_next; mv_st_prev = (mv_stent *)((char *)mv_st_prev + mv_st_prev->mv_st_next); continue; } /* We found a previous mv_stent we need to keep. If we had an interveening mv_stent we don't need to * keep, migrate the new keeper mv_stent adjacent to the previous keeper. */ if (mv_st_prev != (mv_stent *)top) { DBGEHND((stderr, "flush_jmp: Migrating keeper mv_stent from 0x"lvaddr" to 0x"lvaddr" type %d\n", mv_st_prev, top, mv_st_type)); if (MVST_TPHOLD == mv_st_type) { /* If we are moving an MVST_TPHOLD mv_stent, find it in the tpstack and fix its * address there too. Else we won't unwind to the correct place on a restart. */ fix_tphold_mvc(top, (char *)mv_st_prev, ((char *)mv_st_prev + mvs_size[MVST_TPHOLD])); } memmove(top, mv_st_prev, mvs_size[mv_st_type]); } DBGEHND((stderr, "flush_jmp: Updating offsets for mv_stent at addr 0x"lvaddr" type %d\n", mv_st_ent, mv_st_ent->mv_st_type)); mv_st_ent->mv_st_next = mvs_size[mv_st_ent->mv_st_type]; mv_st_ent = (mv_stent *)top; mv_st_ent->mv_st_next += (unsigned int)((char *)mv_st_prev - top); top += mvs_size[mv_st_ent->mv_st_type]; mv_st_prev = (mv_stent *)((char *)mv_st_ent + mv_st_ent->mv_st_next); } shift = (int4)((char *)frame_pointer - top - size); DBGEHND_ONLY(msp_save = msp); if (shift) { if ((unsigned char *)mv_chain + shift <= stackwarn) { if ((unsigned char *)mv_chain + shift <= stacktop) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKOFLOW); else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKCRIT); } DBGEHND((stderr, "flush_jmp: Shifting %d bytes of stack from 0x"lvaddr" to 0x"lvaddr" by %d bytes\n", INTCAST(top - (char *)mv_chain), mv_chain, (mv_chain + shift), shift)); /* Since we are moving one or more mv_stents, it is no more difficult to check the range against the * tp_frame chain than it is to loop through the mv_stents checking each one since the tp_frame stack * is usually no more than 1-3 deep. */ fix_tphold_mvc(((char *)mv_chain + shift), (char *)mv_chain, ((char *)mv_chain + (top - (char *)mv_chain))); memmove((char *)mv_chain + shift, mv_chain, top - (char *)mv_chain); mv_chain = (mv_stent *)((char *)mv_chain + shift); mv_st_ent = (mv_stent *)((char *)mv_st_ent + shift); mv_st_ent->mv_st_next -= shift; msp = (unsigned char *)mv_chain; } memset(frame_pointer->l_symtab, 0, size); DBGEHND((stderr, "flush_jmp: Old msp: 0x"lvaddr" New msp: 0x"lvaddr"\n", msp_save, msp)); return; }
void flush_jmp (rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr) { mv_stent *mv_st_ent, *mv_st_prev; char *top; unsigned char *msp_save; int4 shift, size; error_def(ERR_STACKOFLOW); error_def(ERR_STACKCRIT); unwind_nocounts(); /* We are going to mutate the current frame from the program it was running to the program we want it to run. * If the current frame is marked for indr cache cleanup, do that cleanup now and unmark the frame. */ IF_INDR_FRAME_CLEANUP_CACHE_ENTRY_AND_UNMARK(frame_pointer); frame_pointer->rvector = rtn_base; frame_pointer->vartab_ptr = (char *)VARTAB_ADR(rtn_base); frame_pointer->vartab_len = frame_pointer->rvector->vartab_len; frame_pointer->mpc = transfer_addr; frame_pointer->ctxt = context; #ifdef HAS_LITERAL_SECT frame_pointer->literal_ptr = (int4 *)0; #endif frame_pointer->temp_mvals = frame_pointer->rvector->temp_mvals; size = rtn_base->temp_size; frame_pointer->temps_ptr = (unsigned char *) frame_pointer - size; size += rtn_base->vartab_len * sizeof(mval *); frame_pointer->l_symtab = (mval **)((char *) frame_pointer - size); assert(frame_pointer->type & SFT_COUNT); assert((unsigned char *) mv_chain > stacktop && (unsigned char *) mv_chain <= stackbase); while ((char *) mv_chain < (char *) frame_pointer && !mvs_save[mv_chain->mv_st_type]) { msp = (unsigned char *)mv_chain; op_oldvar(); } if ((char *) mv_chain > (char *) frame_pointer) { msp_save = msp; msp = (unsigned char *)frame_pointer->l_symtab; if (msp <= stackwarn) { if (msp <= stacktop) { msp = msp_save; rts_error(VARLSTCNT(1) ERR_STACKOFLOW); } else rts_error(VARLSTCNT(1) ERR_STACKCRIT); } memset(msp, 0, size); return; } mv_st_ent = mv_chain; mv_st_prev = (mv_stent *)((char *) mv_st_ent + mv_st_ent->mv_st_next); top = (char *) mv_st_ent + mvs_size[mv_st_ent->mv_st_type]; while ((char *) mv_st_prev < (char *) frame_pointer) { if ( !mvs_save[mv_st_prev->mv_st_type] ) { unw_mv_ent(mv_st_prev); mv_st_ent->mv_st_next += mv_st_prev->mv_st_next; mv_st_prev = (mv_stent *)((char *) mv_st_prev + mv_st_prev->mv_st_next); continue; } if (mv_st_prev != (mv_stent *)top) memmove(top, mv_st_prev, mvs_size[mv_st_prev->mv_st_type]); mv_st_ent->mv_st_next = mvs_size[mv_st_ent->mv_st_type]; mv_st_ent = (mv_stent *)top; mv_st_ent->mv_st_next += (char *) mv_st_prev - top; top += mvs_size[mv_st_ent->mv_st_type]; mv_st_prev = (mv_stent *)((char *) mv_st_ent + mv_st_ent->mv_st_next); } shift = (char *) frame_pointer - top - size; if (shift) { if ((unsigned char *)mv_chain + shift <= stackwarn) { if ((unsigned char *)mv_chain + shift <= stacktop) rts_error(VARLSTCNT(1) ERR_STACKOFLOW); else rts_error(VARLSTCNT(1) ERR_STACKCRIT); } memmove((char *) mv_chain + shift, mv_chain, top - (char *) mv_chain); mv_chain = (mv_stent *)((char *) mv_chain + shift); mv_st_ent = (mv_stent *)((char *) mv_st_ent + shift); mv_st_ent->mv_st_next -= shift; msp = (unsigned char *) mv_chain; } memset(frame_pointer->l_symtab, 0, size); return; }
/* Restore given local variable from supplied TP restore entry into given symval. Note lvscan_anchor will only be non-NULL * for the final level we are restoring (but not unwinding). We don't need to restore counters for any vars except the * very last level. * * The return code is only used when unrolling the M stack runs into a trigger base frame which must be unrolled * by gtm_trigger. A non-zero return code signals to tp_unwind() that it needs to rethrow the tprestart error. */ int tp_unwind_restlv(lv_val *curr_lv, lv_val *save_lv, tp_var *restore_ent, lvscan_blk **lvscan_anchor, int *tprestart_rc) { ht_ent_mname *tabent; lv_val *inuse_lv; int elemindx; mv_stent *mvc; lvscan_blk *lvscan, *newlvscan; lvTree *lvt_child; boolean_t var_cloned; assert(curr_lv); assert(LV_IS_BASE_VAR(curr_lv)); assert(curr_lv->tp_var); DBGRFCT((stderr, "\ntp_unwind_restlv: Entered for varname: '%.*s' curr_lv: 0x"lvaddr" save_lv: 0x"lvaddr"\n", restore_ent->key.var_name.len, restore_ent->key.var_name.addr, curr_lv, save_lv)); DBGRFCT((stderr, "tp_unwind_restlv: tp_pointer/current: fp: 0x"lvaddr"/0x"lvaddr" mvc: 0x"lvaddr"/0x"lvaddr " symval: 0x"lvaddr"/0x"lvaddr"\n", tp_pointer->fp, frame_pointer, tp_pointer->mvc, mv_chain, tp_pointer->sym, curr_symval)); /* First get the stack in the position where we can actually process this entry. Need to make sure we are processing * the symbol table we need to be processing so unwind enough stuff to get there. */ if (curr_symval != tp_pointer->sym) { /* Unwind as many stackframes as are necessary up to the max */ while(curr_symval != tp_pointer->sym && frame_pointer < tp_pointer->fp) { # ifdef GTM_TRIGGER if (SFT_TRIGR & frame_pointer->type) { /* We have encountered a trigger base frame. We cannot unroll it because there are C frames * associated with it so we must interrupt this tp_restart and return to gtm_trigger() so * it can unroll the base frame and rethrow the error to properly unroll the C stack. */ *tprestart_rc = ERR_TPRETRY; tprestart_state = TPRESTART_STATE_TPUNW; DBGTRIGR((stderr, "tp_unwind: Encountered trigger base frame during M-stack unwind - " "rethrowing\n")); return -1; } # endif op_unwind(); } if (curr_symval != tp_pointer->sym) { /* Unwind as many mv_stents as are necessary up to the max */ mvc = mv_chain; while(curr_symval != tp_pointer->sym && mvc < tp_pointer->mvc) { unw_mv_ent(mvc); mvc = (mv_stent *)(mvc->mv_st_next + (char *)mvc); } mv_chain = mvc; /* Final check */ if (curr_symval != tp_pointer->sym) GTMASSERT; } } var_cloned = curr_lv->tp_var->var_cloned; if (var_cloned) { /* Var/tree has been copied (and modified) -- see about restoring it */ DBGRFCT((stderr, "\ntp_unwind_restlv: curr_lv was modified and cloned -- needs restoration\n")); if (NULL != restore_ent->key.var_name.addr) { /* Restore data into a named variable (hash table entry) * Step 1 -- find its hash table address to see what lv_val is there now. */ tabent = lookup_hashtab_mname(&((tp_pointer->sym)->h_symtab), &restore_ent->key); assert(tabent); /* Step 2 -- If lv_val is NOT the same as it was, then we must replace the lv_val * currently in use. Decrement its use count (which will delete it and the tree if * it is no longer used) and replace with desired previous lv_val whose use count * was incremented when it was saved. */ if (curr_lv != (inuse_lv = (lv_val *)tabent->value)) { if (inuse_lv) DECR_BASE_REF_RQ(tabent, inuse_lv, FALSE); DBGRFCT((stderr, "tp_unwind: hte 0x"lvaddr" being reset from 0x"lvaddr" to 0x"lvaddr"\n", tabent, tabent->value, curr_lv)); tabent->value = (void *)curr_lv; INCR_TREFCNT(curr_lv); /* Back in the hash table, bump its reference */ } } /* Else, if restoring orphaned data, just prune the old var and copy in the saved tree (if one existed) */ /* Step 3 -- We have the correct lv_val in the hash table now but it has the wrong value. * Get rid of its current tree if any. */ if (lvt_child = LV_GET_CHILD(curr_lv)) /* Note assignment */ { DBGRFCT((stderr, "\ntp_unwind_restlv: Killing children of curr_lv 0x"lvaddr"\n", curr_lv)); assert((lvTreeNode *)curr_lv == LVT_PARENT(lvt_child)); LV_CHILD(curr_lv) = NULL; /* prevent recursion due to alias containers */ lv_killarray(lvt_child, FALSE); } /* Step 4: Copy in the needed fields from the saved flavor lv_val back to curr_lv. * Preserve the ref counts of the current var since the copy's ref counts have not been kept up to date. */ DBGRFCT((stderr, "\ntp_unwind_restlv: Restoring value of lv 0x"lvaddr" back into lv 0x"lvaddr"\n", save_lv, curr_lv)); /* The following is optimized to do the initialization of just the needed structure members. For that it assumes a * particular "lv_val" structure layout. The assumed layout is asserted so any changes to the layout will * automatically show an issue here and cause the below initialization to be accordingly reworked. */ assert(0 == OFFSETOF(lv_val, v)); assert(OFFSETOF(lv_val, v) + SIZEOF(curr_lv->v) == OFFSETOF(lv_val, ptrs)); assert(OFFSETOF(lv_val, ptrs) + SIZEOF(curr_lv->ptrs) == OFFSETOF(lv_val, stats)); assert(OFFSETOF(lv_val, stats) + SIZEOF(curr_lv->stats) == OFFSETOF(lv_val, has_aliascont)); assert(OFFSETOF(lv_val, has_aliascont) + SIZEOF(curr_lv->has_aliascont) == OFFSETOF(lv_val, lvmon_mark)); assert(OFFSETOF(lv_val, lvmon_mark) + SIZEOF(curr_lv->lvmon_mark) == OFFSETOF(lv_val, tp_var)); assert(OFFSETOF(lv_val, tp_var) + SIZEOF(curr_lv->tp_var) == SIZEOF(lv_val)); /* save_lv -> curr_lv Copy begin */ curr_lv->v = save_lv->v; curr_lv->ptrs = save_lv->ptrs; assert(0 < curr_lv->stats.trefcnt); /* No need to copy "stats" as curr_lv is more uptodate */ assert(0 < curr_lv->stats.crefcnt); assert(8 == (OFFSETOF(lv_val, tp_var) - OFFSETOF(lv_val, has_aliascont))); /* lv_val->has_aliascont/lvmon_mark all initialized in one shot. * Note: We use "qw_num *" instead of "uint8 *" below because the former works on 32-bit platforms too. * On 64-bit platforms, this is 1 8-byte move instead of 2 4-byte moves (the latter means more instructions). * Even though lvmon_mark is maintained only if DEBUG_ALIAS is #defined, we do the copy here unconditionally * because on 64-bit platforms, a 4-byte copy (of just has_aliascont) is the same as an 8-byte copy * (of has_aliascont and lvmon_mark). On 32-bit platforms, it saves us one copy but we dont worry about it much. */ GTM64_ONLY(assert(IS_PTR_8BYTE_ALIGNED(&curr_lv->has_aliascont));) NON_GTM64_ONLY(assert(IS_PTR_4BYTE_ALIGNED(&curr_lv->has_aliascont));)