void new_stack_frame(rhdtyp *rtn_base, unsigned char *context, unsigned char *transfer_addr) { register stack_frame *sf; unsigned char *msp_save; unsigned int x1, x2; assert(NULL != rtn_base); assert(NULL != transfer_addr); assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); msp_save = msp; sf = (stack_frame *)(msp -= SIZEOF(stack_frame)); 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); } assert((unsigned char *)msp < stackbase); sf->old_frame_pointer = frame_pointer; sf->rvector = rtn_base; sf->vartab_ptr = (char *)VARTAB_ADR(rtn_base); sf->vartab_len = sf->rvector->vartab_len; sf->ctxt = context; sf->mpc = transfer_addr; sf->flags = 0; SET_GLVN_INDX(sf, GLVN_POOL_UNTOUCHED); sf->ret_value = NULL; sf->dollar_test = -1; # ifdef HAS_LITERAL_SECT sf->literal_ptr = (int4 *)LITERAL_ADR(rtn_base); # endif sf->temp_mvals = sf->rvector->temp_mvals; msp -= x1 = rtn_base->temp_size; sf->temps_ptr = msp; sf->type = SFT_COUNT; msp -= x2 = rtn_base->vartab_len * SIZEOF(ht_ent_mname *); sf->l_symtab = (ht_ent_mname **)msp; 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); } assert(msp < stackbase); memset(msp, 0, x1 + x2); frame_pointer = sf; assert((frame_pointer < frame_pointer->old_frame_pointer) || (NULL == frame_pointer->old_frame_pointer)); DBGEHND((stderr, "new_stack_frame: Added stackframe at addr 0x"lvaddr" old-msp: 0x"lvaddr" new-msp: 0x"lvaddr " for routine %.*s (rtnhdr 0x"lvaddr")\n", sf, msp_save, msp, rtn_base->routine_name.len, rtn_base->routine_name.addr, rtn_base)); 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, 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; }