void urx_resolve(rhdtyp *rtn, lab_tabent *lbl_tab, lab_tabent *lbl_top) { urx_rtnref *rp0, *rp1; urx_labref *lp0, *lp1; urx_addr *ap; if (!urx_getrtn(rtn->routine_name.addr, rtn->routine_name.len, &rp0, &rp1, &urx_anchor)) return; while ((ap = rp1->addr) != 0) { assert(0 == *ap->addr); #ifdef VMS *ap->addr = (int4)rtn->linkage_ptr; #else *ap->addr = (UINTPTR_T)rtn; #endif rp1->addr = ap->next; free(ap); } while (lbl_tab < lbl_top) { if (urx_getlab(lbl_tab->lab_name.addr, lbl_tab->lab_name.len, rp1, &lp0, &lp1)) { while (0 != (ap = lp1->addr)) /* note the assignment! */ { assert(0 == *ap->addr); *ap->addr = USHBIN_ONLY((INTPTR_T)&lbl_tab->lnr_adr) /* on non-shared binary resolve this address by adding the offset stored at lbl_tab address * to the routine header, to arrive at the address at which the current line number entry is * stored */ NON_USHBIN_ONLY((INTPTR_T)&lbl_tab->lab_ln_ptr); lp1->addr = ap->next; free(ap); } assert(0 == lp1->addr); if (lp0 == (urx_labref *)rp1) ((urx_rtnref *)lp0)->lab = lp1->next; else lp0->next = lp1->next; free(lp1); } lbl_tab++; } if (0 == rp1->lab) { rp0->next = rp1->next; free(rp1); } }
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; }
/* Routine to eliminate the zlinked trigger code for a given trigger about to be deleted. Operations performed * differ depending on platform type (shared binary or not). */ void gtm_trigger_cleanup(gv_trigger_t *trigdsc) { rtn_tabent *rbot, *mid, *rtop; mident *rtnname; rhdtyp *rtnhdr; textElem *telem; int comp, size; stack_frame *fp, *fpprev; mname_entry key; ht_ent_mname *tabent; routine_source *src_tbl; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* First thing to do is release trigger source field if it exists */ if (0 < trigdsc->xecute_str.str.len) { free(trigdsc->xecute_str.str.addr); trigdsc->xecute_str.str.len = 0; trigdsc->xecute_str.str.addr = NULL; } /* Next thing to do is find the routine header in the rtn_names list so we can remove it. */ rtnname = &trigdsc->rtn_desc.rt_name; rtnhdr = trigdsc->rtn_desc.rt_adr; rbot = rtn_names; rtop = rtn_names_end; for (;;) { /* See if routine exists in list via a binary search which reverts to serial search when # of items drops below the threshold S_CUTOFF. */ if ((rtop - rbot) < S_CUTOFF) { comp = -1; for (mid = rbot; mid <= rtop ; mid++) { MIDENT_CMP(&mid->rt_name, rtnname, comp); if (0 == comp) break; if (0 < comp) GTMASSERT; /* Routine should be found */ } break; } else { mid = rbot + (rtop - rbot)/2; MIDENT_CMP(&mid->rt_name, rtnname, comp); if (0 == comp) break; else if (0 > comp) { rbot = mid + 1; continue; } else { rtop = mid - 1; continue; } } } # ifdef DEBUG assert(rtnhdr == mid->rt_adr); /* Verify trigger routine we want to remove is not currently active. If it is, we need to GTMASSERT. * Triggers are not like regular routines since they should only ever be referenced from the stack during a * transaction. Likewise, we should only ever load the triggers as the first action in that transaction. */ for (fp = frame_pointer; fp ; fp = fpprev) { fpprev = fp->old_frame_pointer; # ifdef GTM_TRIGGER if (NULL != fpprev && SFT_TRIGR & fpprev->type) fpprev = *(stack_frame **)(fpprev + 1); # endif /* Only one possible version of a trigger routine */ assert(USHBIN_ONLY(NULL) NON_USHBIN_ONLY(fp->rvector) == OLD_RHEAD_ADR(CURRENT_RHEAD_ADR(fp->rvector))); assert(fp->rvector != rtnhdr); } # endif /* Remove break points in this routine before rmv from rtntbl */ zr_remove(rtnhdr, BREAKMSG); /* Release any $TEXT() info this trigger has loaded */ if (NULL != (TREF(rt_name_tbl)).base) { key.var_name = mid->rt_name; COMPUTE_HASH_MNAME(&key); if (NULL != (tabent = lookup_hashtab_mname(TADR(rt_name_tbl), &key))) /* note assignment */ { /* We have a hash entry. Whether it has a value or not, it has a key name (if this is * the first time this trigger is being deleted) that may be pointing into the routine we * are about to remove. Migrate this key name to the stringpool. */ s2pool(&tabent->key.var_name); if (NULL != tabent->value) { /* Has value, release the source. Entries and source are malloc'd in two blocks on UNIX */ src_tbl = (routine_source *)tabent->value; if (NULL != src_tbl->srcbuff) free(src_tbl->srcbuff); free(src_tbl); tabent->value = NULL; } } } /* Remove the routine from the rtn_table */ size = INTCAST((char *)rtn_names_end - (char *)mid); if (0 < size) memmove((char *)mid, (char *)(mid + 1), size); /* Remove this routine name from sorted table */ rtn_names_end--; urx_remove(rtnhdr); /* Remove any unresolved entries */ # ifdef USHBIN_SUPPORTED stp_move((char *)rtnhdr->literal_text_adr, (char *)(rtnhdr->literal_text_adr + rtnhdr->literal_text_len)); GTM_TEXT_FREE(rtnhdr->ptext_adr); /* R/O releasable section */ free(rtnhdr->literal_adr); /* R/W releasable section part 1 */ free(rtnhdr->linkage_adr); /* R/W releasable section part 2 */ free(rtnhdr->labtab_adr); /* Usually non-releasable but triggers don't have labels so * this is just cleaning up a dangling null malloc */ free(rtnhdr); # else # if (!defined(__linux__) && !defined(__CYGWIN__)) || !defined(__i386) || !defined(COMP_GTA) # error Unsupported NON-USHBIN platform # endif /* For a non-shared binary platform we need to get an approximate addr range for stp_move. This is not * done when a routine is replaced on these platforms but in this case we are able to due to the isolated * environment if we only take the precautions of migrating potential literal text which may have been * pointed to by any set environment variables. * In this format, the only platform we support currently is Linux-x86 (i386) which uses GTM_TEXT_ALLOC * to allocate special storage for it to put executable code in. We can access the storage header for * this storage and find out how big it is and use that information to give stp_move a good range since * the literal segment occurs right at the end of allocated storage (for which there is no pointer * in the fileheader). */ telem = (textElem *)((char *)rtnhdr - offsetof(textElem, userStorage)); assert(TextAllocated == telem->state); stp_move((char *)LNRTAB_ADR(rtnhdr) + (rtnhdr->lnrtab_len * SIZEOF(lnr_tabent)), (char *)rtnhdr + telem->realLen); GTM_TEXT_FREE(rtnhdr); # endif }