/**************************************************************** * * * Copyright 2011, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #include "mdef.h" #include "dollar_zlevel.h" #include "error_trap.h" #include "golevel.h" #include "cache.h" #include "hashtab.h" #include "hashtab_objcode.h" #include "hashtab_mname.h" #include #include "stack_frame.h" #include "mprof.h" #include "gtm_unlink_all.h" #include "zbreak.h" #include "gtm_text_alloc.h" #include "parse_file.h" #include "zro_shlibs.h" #include "gdsroot.h" #include "gdsbt.h" #include "gdsblk.h" #include "gdsbml.h" #include "gtm_facility.h" #include "fileinfo.h" #include "gdsfhead.h" #include "srcline.h" #include "gtmci.h" /* for GTM_CIMOD */ #include "dm_setup.h" /* for GTM_DMOD */ #include "urx.h" #include "stringpool.h" #ifdef GTM_TRIGGER #include "gv_trigger.h" #include "gtm_trigger.h" #include "gv_trigger_protos.h" #endif GBLREF boolean_t is_tracing_on; GBLREF gv_key *gv_currkey; GBLREF hash_table_objcode cache_table; GBLREF int dollar_truth; GBLREF int indir_cache_mem_size; GBLREF rtn_tabent *rtn_names, *rtn_names_end, *rtn_names_top, *rtn_fst_table; GBLREF stack_frame *frame_pointer; GBLREF gv_namehead *gv_target_list; /* Routine to do the following: * * 1. Stop M-Profiling. * 2. Unwind the M stack back to level 1 * 3. (re)Initialize $ECODE, $REFERENCE and $TEST. * 4. Remove all triggers. This includes not only the trigger routines but the trigger definitions and * linkages in the gvt_trigger struct anchored off of the gv_target. * 5. Unlink all M routines. This includes getting rid of their linkage entries, breakpoints, * and $TEXT() caches and re-initializing the routine table. * 6. Empty the indirect cache. * 7. Close the shared libraries associated with M programs and reopen them after the unroll if any * are present in $ZROUTINES. * * Currently called from op_zgoto() but could be called from elsewhere if needed in the future. * * Note this routine removes all loaded programs but the intial base-frame on the stack has the address of whatever * the first program is (GTM$DMOD in a direct mode situation or the starting program in a -run invocation). It is up * to the caller to set the base frame with this address so the stack has no unknown references in it. */ void gtm_unlink_all(void) { rtn_tabent *rtab; rhdtyp *rtnhdr, *rhdr, *next_rhdr; ht_ent_mname *tabent_mname; textElem *telem; ht_ent_objcode *tabent_obj, *topent; cache_entry *csp; mname_entry key; routine_source *src_tbl; gv_namehead *gvt; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* Step 1: Stop M-Profiling */ if (is_tracing_on) turn_tracing_off(NULL); /* Step 2: Unwind M stack back to level 1 */ GOLEVEL(1, TRUE); assert(1 == dollar_zlevel()); /* Step 3: re-Initialize $ECODE, $REFERENCE, and $TEST */ NULLIFY_DOLLAR_ECODE; /* Clears $ECODE and results returned for $STACK */ if (NULL != gv_currkey) gv_currkey->end = 0; /* Clears $REFERENCE */ dollar_truth = FALSE; /* aka $TEST */ /* Step 4: Remove all triggers */ # ifdef GTM_TRIGGER for (gvt = gv_target_list; gvt; gvt = gvt->next_gvnh) gvtr_free(gvt); # endif /* Step 5: Unlink all routines, remove $TEXT cache and remove breakpoints.Note that for the purposes of this section, * there is no difference between normal routines and trigger routines. Both are being removed completely so the code * below is a hodgepodge of code from zlput_rname and gtm_trigger_cleanup(). Note process in reverse order so we can * move rtn_names_end up leaving processed entries (whose keys no longer work) off the end of the table without moving * anything. This is necessary because removing ZBREAK points can call find_rtn_hdr so this table needs to remain in * a usable state while we are doing this. */ for (rtab = rtn_names_end; rtab > rtn_names; rtab--, rtn_names_end = rtab) { /* [0] is not used (for some reason) */ rtnhdr = rtab->rt_adr; zr_remove(rtnhdr, FALSE); /* Remove all breakpoints in this routine */ urx_remove(rtnhdr); /* Remove all unresolved entries for this routine */ /* If source has been read in for this routine, free the space. Since routine name is the key, do this before * (in USHBIN builds) we release the literal text section as part of the releasable read-only section. * Note this code is similar to code in zlput_rname() 'cept this is necessarily UNIX-only. */ tabent_mname = NULL; if (NULL != (TREF(rt_name_tbl)).base) { key.var_name = rtab->rt_name; COMPUTE_HASH_MNAME(&key); if (NULL != (tabent_mname = lookup_hashtab_mname(TADR(rt_name_tbl), &key)) && tabent_mname->value) { /* Entries and source are malloc'd in two blocks on UNIX */ src_tbl = (routine_source *)tabent_mname->value; if (NULL != src_tbl->srcbuff) free(src_tbl->srcbuff); free(src_tbl); tabent_mname->value = NULL; } } if ((0 == strcmp(rtnhdr->routine_name.addr, GTM_DMOD)) || (0 == strcmp(rtnhdr->routine_name.addr, GTM_CIMOD))) { /* If the routine is GTM$DMOD or GTM$CIMOD, it is allocated in one chunk by make_*mode(). Release it in * one chunk too. */ GTM_TEXT_FREE(rtnhdr); } else { # ifdef USHBIN_SUPPORTED /* We are about to release program areas containing literal text that could be pointed to by * local var mvals that are being kept so migrate program literals to the stringpool. Note zlput_rname() * only does this if not a shared library but since we are releasing shared libraries too, do it * regardless. */ if (0 < rtnhdr->literal_text_len) { stp_move((char *)rtnhdr->literal_text_adr, (char *)(rtnhdr->literal_text_adr + rtnhdr->literal_text_len)); } if (NULL == rtnhdr->shlib_handle) /* We can only release this section if this is not a shared library */ 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 not in this case */ /* Run the chain of old (replaced) versions freeing them also */ for (rhdr = OLD_RHEAD_ADR(rtnhdr); NULL != rhdr; rhdr = next_rhdr) { next_rhdr = rhdr->old_rhead_adr; free(rhdr->labtab_adr); /* Free dangling label table */ free(rhdr); } 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 need to since the routines are * going away which will cause problems with any local variables or environment varspointing to these * literals. * * 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). (Note we allow CYGWIN in here too but it has not been tested at this time) */ 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); /* Run the chain of old (replaced) versions freeing them first */ for (rhdr = OLD_RHEAD_ADR(rtnhdr); rtnhdr != rhdr; rhdr = next_rhdr) { next_rhdr = (rhdtyp *)rhdr->old_rhead_ptr; GTM_TEXT_FREE(rhdr); } GTM_TEXT_FREE(rtnhdr); # endif } } /* All programs have been removed. If this is the "first" table allocated which cannot be removed, just reinitialize * the table and we're done. If a new table, release it, recover the first table, initialize and we're done. */ if (rtn_names != rtn_fst_table) { free(rtn_names); rtn_names = rtn_fst_table; assert(NULL != rtn_fst_table); } memset(rtn_names, 0, SIZEOF(rtn_tabent)); rtn_names_end = rtn_names_top = rtn_names; /* Step 5: Empty the indirect cache */ for (tabent_obj = cache_table.base, topent = cache_table.top; tabent_obj < topent; tabent_obj++) { /* Run through the hashtable getting rid of all the entries. Not bothering with the cleanups * like are done in cache_table_rebuild since we are going to re-init the table. */ if (HTENT_VALID_OBJCODE(tabent_obj, cache_entry, csp)) { GTM_TEXT_FREE(csp); } } reinitialize_hashtab_objcode(&cache_table); /* Completely re-initialize the hash table */ indir_cache_mem_size = 0; /* Step 6: Close M code shared libraries */ zro_shlibs_unlink_all(); }