Пример #1
0
/* For routine name given, return routine header address if rhd not already set */
rhdtyp	*op_rhdaddr(mval *name, rhdtyp *rhd)
{
	mval		routine;
	mident_fixed	routname;
	rhdtyp		*answer;

	if (NULL != rhd)
		answer = rhd;
	else
	{
		MV_FORCE_STR(name);
		routine = *name;
		routine.str.len = (MAX_MIDENT_LEN < routine.str.len ? MAX_MIDENT_LEN : routine.str.len);
		memcpy(&routname.c[0], routine.str.addr, routine.str.len);
		routine.str.addr = (char *)&routname.c[0];
		if ((NULL == rtn_names) || (NULL == (answer = find_rtn_hdr(&routine.str))))	/* Note assignment */
		{	/* Initial check for rtn_names is so we avoid the call to find_rtn_hdr() if we have just
			 * unlinked all modules as find_rtn_hdr() does not deal well with an empty rtn table.
			 */
			op_zlink(&routine, NULL);
			answer = find_rtn_hdr(&routine.str);
			if (NULL == answer)
				rts_error(VARLSTCNT(8) ERR_ZLINKFILE, 2, name->str.len, name->str.addr,
					ERR_ZLMODULE, 2, strlen(&zlink_mname.c[0]), &zlink_mname);
#			if defined (__alpha) && defined (__vms)
			answer = answer->linkage_ptr;
			if (NULL == answer)
				rts_error(VARLSTCNT(8) ERR_ZLINKFILE, 2, name->str.len, name->str.addr,
					ERR_ZLMODULE, 2, strlen(&zlink_mname.c[0]), zlink_mname.c);
#			endif
		}
	}
	return answer;
}
Пример #2
0
void job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr)
{
    rhdtyp		*rt_hdr;
    int4		*lp;
    error_def	(ERR_JOBLABOFF);

    if ((rt_hdr = find_rtn_hdr(rtn)) == 0)
    {
        mval rt;

        rt.mvtype = MV_STR;
        rt.str = *rtn;
        op_zlink(&rt,0);
        if ((rt_hdr = find_rtn_hdr (rtn)) == 0)
            GTMASSERT;
    }
    lp = NULL;
    if ((rt_hdr->compiler_qlf & CQ_LINE_ENTRY) || 0 == offset)
    {   /* label offset with routine compiled with NOLINE_ENTRY should cause error */
        lp = find_line_addr(rt_hdr, label, offset, NULL);
    }
    if (!lp)
        rts_error(VARLSTCNT(1) ERR_JOBLABOFF);
    *labaddr = (char *) LINE_NUMBER_ADDR(rt_hdr, lp);
    *hdr = (char *)rt_hdr;
}
Пример #3
0
/* Routine to locate the entry being linked from generated code and link it in and return to the glue code
 * code to drive it.
 *
 * Arguments:
 *
 *   rtnhdridx  - Index into linkage table for the routine that needs linking.
 */
void auto_zlink(int rtnhdridx)
{
	mstr		rname;
	mident_fixed	rname_buff;
	mval		rtn;
	rhdtyp		*rhd;

	assert(0 <= rtnhdridx);			/* rtnhdridx must never be negative */
	assert(rtnhdridx <= frame_pointer->rvector->linkage_len);
	assert(NULL == frame_pointer->rvector->linkage_adr[rtnhdridx].ext_ref);
	rname = frame_pointer->rvector->linkage_names[rtnhdridx];
	rname.addr += (INTPTR_T)frame_pointer->rvector->literal_text_adr;	/* Perform relocation on name */
	memcpy(rname_buff.c, rname.addr, rname.len);
	memset(rname_buff.c + rname.len, 0, SIZEOF(rname_buff) - rname.len);	/* Clear rest of mident_fixed */
	rname.addr = rname_buff.c;
	assert(rname.len <= MAX_MIDENT_LEN);
	assert(NULL == find_rtn_hdr(&rname));
	rtn.mvtype = MV_STR;
	rtn.str.len = rname.len;
	rtn.str.addr = rname.addr;
	op_zlink(&rtn, NULL);			/* op_zlink() takes care of '%' -> '_' translation of routine name */
	DEBUG_ONLY(if ('_' == rname_buff.c[0]) rname_buff.c[0] = '%');
	assert(NULL != (rhd = find_rtn_hdr(&rname)));
	DBGARLNK((stderr, "auto_zlink: Linked in rtn %.*s to "lvaddr"\n", rname.len, rname.addr, find_rtn_hdr(&rname)));
	return;
}
Пример #4
0
boolean_t job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr, boolean_t *need_rtnobj_shm_free)
{
	rhdtyp		*rt_hdr;
	int4		*lp;
	mval		rt;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	if (NULL == (rt_hdr = find_rtn_hdr(rtn)))
	{
		rt.mvtype = MV_STR;
		rt.str = *rtn;
		op_zlink(&rt, NULL);
		rt_hdr = find_rtn_hdr(rtn);
		if (NULL == rt_hdr)
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_ZLINKFILE, 2, rtn->len, rtn->addr,
				      ERR_ZLMODULE, 2, STRLEN(&zlink_mname.c[0]), &zlink_mname);
		*need_rtnobj_shm_free = ARLINK_ONLY(rt_hdr->shared_object) NON_ARLINK_ONLY(FALSE);
		*hdr = (char *)rt_hdr;
	} else
		*need_rtnobj_shm_free = FALSE;
	lp = NULL;
	if ((rt_hdr->compiler_qlf & CQ_LINE_ENTRY) || (0 == offset))
		/* Label offset with routine compiled with NOLINE_ENTRY should cause error. */
		lp = find_line_addr(rt_hdr, label, offset, NULL);
	if (!lp)
		return (FALSE);
	/* Set the pointer to address / offset for line number entry storage in TABENT_PROXY. */
#	ifdef USHBIN_SUPPORTED
	ARLINK_ONLY((TABENT_PROXY).rtnhdr_adr = rt_hdr);
	(TABENT_PROXY).lnr_adr = lp;
#	else
	/* On non-shared-binary, calculcate the offset to the corresponding lnr_tabent record by subtracting
	 * the base address (routine header) from line number entry's address, and save the result in
	 * lab_ln_ptr field of TABENT_PROXY structure.
	 */
	(TABENT_PROXY).lab_ln_ptr = ((int4)lp - (int4)rt_hdr);
#	endif
	if (NULL != labaddr)
		*labaddr = (char *)LINE_NUMBER_ADDR(rt_hdr, lp);
	*hdr = (char *)rt_hdr;
	return (TRUE);
}
Пример #5
0
void job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr)
{
	rhdtyp		*rt_hdr;
	int4		*lp;
	mval		rt;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	if (NULL == (rt_hdr = find_rtn_hdr(rtn)))
	{
		rt.mvtype = MV_STR;
		rt.str = *rtn;
		op_zlink(&rt, NULL);
		assertpro(NULL != (rt_hdr = find_rtn_hdr(rtn)));
	}
	lp = NULL;
	if ((rt_hdr->compiler_qlf & CQ_LINE_ENTRY) || (0 == offset))
		/* Label offset with routine compiled with NOLINE_ENTRY should cause error. */
		lp = find_line_addr(rt_hdr, label, offset, NULL);
	if (!lp)
		rts_error(VARLSTCNT(1) ERR_JOBLABOFF);
	/* Set the pointer to address / offset for line number entry storage in lab_proxy. */
	USHBIN_ONLY((TREF(lab_proxy)).lnr_adr = lp;)
Пример #6
0
void job_addr(mstr *rtn, mstr *label, int4 offset, char **hdr, char **labaddr)
{
	rhdtyp		*rt_hdr;
	int4		*lp;
	error_def	(ERR_JOBLABOFF);

	if ((rt_hdr = find_rtn_hdr(rtn)) == 0)
	{
		mval rt;

		rt.mvtype = MV_STR;
		rt.str = *rtn;
		op_zlink(&rt,0);
		if ((rt_hdr = find_rtn_hdr (rtn)) == 0)
			GTMASSERT;
	}
	lp = NULL;
	if (!rt_hdr->label_only || 0 == offset)  /* If label_only and offset != 0 should cause error */
		lp = find_line_addr (rt_hdr, label, offset);
	if (!lp)
		rts_error(VARLSTCNT(1) ERR_JOBLABOFF);
	*labaddr = (char *) LINE_NUMBER_ADDR(rt_hdr, lp);
	*hdr = (char *)rt_hdr;
}
Пример #7
0
void op_zprint(mval *rtn, mval *start_label, int start_int_exp, mval *end_label, int end_int_exp)
/* contains label to be located or null string		*/
/* contains label offset or line number to reference	*/
/* contains routine to look in or null string		*/
/* NOTE: If only the first label is specified, the 	*/
/*	 parser makes the second label the duplicate	*/
/*	 of the first. (not so vice versa)		*/
{
	mval	print_line, null_str;
	mstr	*src1, *src2;
	uint4	stat1, stat2;
	rhdtyp	*rtn_vector;
	error_def(ERR_FILENOTFND);
	error_def(ERR_TXTSRCMAT);
	error_def(ERR_ZPRTLABNOTFND);
	error_def(ERR_ZLINKFILE);
	error_def(ERR_ZLMODULE);

	MV_FORCE_STR(start_label);
	MV_FORCE_STR(end_label);
	MV_FORCE_STR(rtn);
	if (NULL == (rtn_vector = find_rtn_hdr(&rtn->str)))
	{
		op_zlink(rtn, NULL);
		rtn_vector = find_rtn_hdr(&rtn->str);
		if (NULL == rtn_vector)
			rts_error(VARLSTCNT(8) ERR_ZLINKFILE, 2, rtn->str.len, rtn->str.addr,
					ERR_ZLMODULE, 2, mid_len(&zlink_mname), &zlink_mname.c[0]);
	}
	stat1 = get_src_line(rtn, start_label, start_int_exp, &src1);
	if (stat1 & LABELNOTFOUND)
		rts_error(VARLSTCNT(1) ERR_ZPRTLABNOTFND);
	if (stat1 & SRCNOTFND)
		rts_error(VARLSTCNT(4) ERR_FILENOTFND, 2, rtn_vector->src_full_name.len, rtn_vector->src_full_name.addr);
	if (stat1 & (SRCNOTAVAIL | AFTERLASTLINE))
		return;
	if (stat1 & (ZEROLINE | NEGATIVELINE))
	{
		null_str.mvtype = MV_STR;
		null_str.str.len = 0;
		stat1 = get_src_line(rtn, &null_str, 1, &src1);
		if (stat1 & AFTERLASTLINE)		/* the "null" file */
			return;
	}
	if (end_int_exp == 0 && (end_label->str.len == 0 || *end_label->str.addr == 0))
		stat2 = AFTERLASTLINE;
	else if ((stat2 = get_src_line(rtn, end_label, end_int_exp, &src2)) & LABELNOTFOUND)
		rts_error(VARLSTCNT(1) ERR_ZPRTLABNOTFND);
	if (stat2 & (ZEROLINE | NEGATIVELINE))
		return;
	if (stat2 & AFTERLASTLINE)
	{
		null_str.mvtype = MV_STR;
		null_str.str.len = 0;
		stat2 = get_src_line(rtn, &null_str, 1, &src2);
		/* number of lines less one for duplicated zero'th line and one due
		   to termination condition being <=
		*/
		assert((INTPTR_T)src2 > 0);
		src2 += rtn_vector->lnrtab_len - 2;
	}
	if (stat1 & CHECKSUMFAIL)
	{
		rts_error(VARLSTCNT(1) INFO_MSK(ERR_TXTSRCMAT));
		op_wteol(1);
	}
	print_line.mvtype = MV_STR;
	for ( ; src1 <= src2 ; src1++)
	{
		if (outofband)
			outofband_action(FALSE);
		print_line.str.addr = src1->addr;
		print_line.str.len = src1->len;
		op_write(&print_line);
		op_wteol(1);
	}
	return;
}
Пример #8
0
void op_fntext(mval *label, int int_exp, mval *rtn, mval *ret)
/* label contains label to be located or null string */
/* int_exp contains label offset or line number to reference */
/* ret is used to return the correct string to caller */
{
	char		*cp, *ctop;
	int		i, lbl, letter;
	mval		*temp_rtn, temp_mval;
	mstr		*sld;
	uint4		stat;
	rhdtyp		*rtn_vector;

	error_def(ERR_TXTNEGLIN);
	error_def(ERR_TXTSRCMAT);
	error_def(ERR_ZLINKFILE);
	error_def(ERR_ZLMODULE);

	MV_FORCE_STR(label);
	MV_FORCE_STR(rtn);
	temp_rtn = &temp_mval;
	*temp_rtn = *rtn;	/* make a copy of the routine in case the caller used the same mval for rtn and ret */
	ret->str.len = 0;	/* make ret an emptystring in case the return is by way of the condition handler */
	ret->mvtype = MV_STR;
	sld = (mstr *)NULL;
	ESTABLISH(fntext_ch);	/* to swallow errors and permit an emptystring result */
	if ((int_exp == 0) && ((label->str.len == 0) || (*label->str.addr == 0)))
		stat = ZEROLINE;
	else
		stat = get_src_line(temp_rtn, label, int_exp, &sld);
	if ((FALSE == (stat & CHECKSUMFAIL)) && (FALSE == (stat & NEGATIVELINE)))
	{
		if (stat & ZEROLINE)
		{
			if (NULL == (rtn_vector = find_rtn_hdr(&temp_rtn->str)))
			{	/* not here, so try to bring it in */
				op_zlink(temp_rtn, 0);
				rtn_vector = find_rtn_hdr(&temp_rtn->str);
			}
			if (NULL != rtn_vector)
			{
				ret->str.addr = cp = (char *)&rtn_vector->routine_name;
				for (ctop = cp + sizeof(mident);  *cp && cp < ctop;  cp++)
					;
				ret->str.len = cp - ret->str.addr;
			}
		} else  if (NULL != sld)
			ret->str = *sld;
	}
	REVERT;
	/* If non-empty, copy result to stringpool and
	 * convert any tabs in linestart to spaces
	 */
	if (ret->str.len)
	{
		if (stringpool.free + ret->str.len > stringpool.top)
				stp_gcol(ret->str.len);
		cp = stringpool.free;
		for (i = 0, lbl = 1; i < ret->str.len; i++)
		{
			letter = ret->str.addr[i];
			if (lbl)
			{
				if ((' ' == letter) || ('\t' == letter))
				{
					letter = ' ';
					lbl = 0;
				}
				*cp++ = letter;
			} else
			{
				if ((' ' != letter) && ('\t' != letter))
				{
					memcpy(cp, &ret->str.addr[i], ret->str.len - i);
					break;
				} else
					*cp++ = ' ';
			}
		}
		ret->str.addr=stringpool.free;
		stringpool.free += ret->str.len;
	}
	return;
}
Пример #9
0
rhdtyp *auto_zlink (unsigned char *pc, int4 **line)
{
	char		*adj_pc;	/* address of PEA rtnref offset */
	mstr		rname;
	mident_fixed	rname_local;
	urx_rtnref	*rtnurx;
	mval		rtn;
	rhdtyp		*rhead;
	union
	{
		ModR_M		modrm;
		unsigned char	byte;
	} modrm_byte_byte, modrm_byte_long;

	/* ASSUMPTION -- The instruction previous to the current mpc is a transfer table jump.
	 *		This is either a byte or a int4 displacement off of ebx, instruction
	 *		size either 3 or 6 (prefix byte, ModR/M byte, 8- or 32-bit offset).
	 */
	modrm_byte_byte.modrm.reg_opcode = I386_INS_CALL_Ev;
	modrm_byte_byte.modrm.mod = I386_MOD32_BASE_DISP_8;
	modrm_byte_byte.modrm.r_m = I386_REG_EBX;
	modrm_byte_long.modrm.reg_opcode = I386_INS_CALL_Ev;
	modrm_byte_long.modrm.mod = I386_MOD32_BASE_DISP_32;
	modrm_byte_long.modrm.r_m = I386_REG_EBX;
	if ((*(pc - XFER_BYTE_SZ) == I386_INS_Grp5_Prefix) && (*(pc - XFER_BYTE_SZ + 1) == modrm_byte_byte.byte))
	{
		assert(*(pc - XFER_BYTE_SZ - PEA_SZ) == I386_INS_PUSH_Iv);
		adj_pc = (char *)pc - XFER_BYTE_SZ - PEA_SZ;
	} else if ((*(pc - XFER_LONG_SZ) == I386_INS_Grp5_Prefix) && (*(pc - XFER_LONG_SZ + 1) == modrm_byte_long.byte))
	{
		assert(*(pc - XFER_LONG_SZ - PEA_SZ) == I386_INS_PUSH_Iv);
		adj_pc = (char *)pc - XFER_LONG_SZ - PEA_SZ;
	} else
		GTMASSERT;
	if (azl_geturxrtn(adj_pc + INST_SZ, &rname, &rtnurx))
	{
		assert((0 <= rname.len) && (MAX_MIDENT_LEN >= rname.len));
		assert(rname.addr);
		/* Copy rname into local storage because azl_geturxrtn sets rname.addr to an address that is
		 * freed during op_zlink and before the call to find_rtn_hdr.
		 */
		memcpy(rname_local.c, rname.addr, rname.len);
		rname.addr = rname_local.c;
		assert(rtnurx);
		assert(*(adj_pc - PEA_SZ) == I386_INS_PUSH_Iv);
		assert(azl_geturxlab(adj_pc - PEA_SZ + INST_SZ, rtnurx));
		assert(!find_rtn_hdr(&rname));
		rtn.mvtype = MV_STR;
		rtn.str.len = rname.len;
		rtn.str.addr = rname.addr;
		op_zlink (&rtn, 0);
		if (0 != (rhead = find_rtn_hdr(&rname)))	/* note the assignment */
		{
			*line = *(int4 **)(adj_pc - PEA_SZ + INST_SZ);
			if (!(*line))
				rts_error(VARLSTCNT(1) ERR_LABELUNKNOWN);
			return rhead;
		}
	}
	rts_error(VARLSTCNT(1) ERR_ROUTINEUNKNOWN);
	return NULL;
}
Пример #10
0
int gtm_trigger_complink(gv_trigger_t *trigdsc, boolean_t dolink)
{
	char		rtnname[GTM_PATH_MAX + 1], rtnname_template[GTM_PATH_MAX + 1];
	char		objname[GTM_PATH_MAX + 1];
	char		zcomp_parms[(GTM_PATH_MAX * 2) + SIZEOF(mident_fixed) + SIZEOF(OBJECT_PARM) + SIZEOF(NAMEOFRTN_PARM)];
	mstr		save_zsource;
	int		rtnfd, rc, lenrtnname, lenobjname, len, alphnum_len, retry, save_errno;
	char		*mident_suffix_p1, *mident_suffix_p2, *mident_suffix_top, *namesub1, *namesub2, *zcomp_parms_ptr;
	mval		zlfile, zcompprm;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	DBGTRIGR_ONLY(memcpy(rtnname, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len));
	DBGTRIGR_ONLY(rtnname[trigdsc->rtn_desc.rt_name.len] = 0);
	DBGTRIGR((stderr, "gtm_trigger_complink: (Re)compiling trigger %s\n", rtnname));
	ESTABLISH_RET(gtm_trigger_complink_ch, ((0 == error_condition) ? TREF(dollar_zcstatus) : error_condition ));
	 /* Verify there are 2 available chars for uniqueness */
	assert((MAX_MIDENT_LEN - TRIGGER_NAME_RESERVED_SPACE) >= (trigdsc->rtn_desc.rt_name.len));
	assert(NULL == trigdsc->rtn_desc.rt_adr);
	gtm_trigger_comp_prev_run_time = run_time;
	run_time = TRUE;	/* Required by compiler */
	/* Verify the routine name set by MUPIP TRIGGER and read by gvtr_db_read_hasht() is not in use */
	if (NULL != find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
	{	/* Ooops .. need name to be more unique.. */
		/* Though variable definitions are conventionally done at the function entry, the reason alphanumeric_table
		 * definition is done here is to minimize the time taken to initialize the below table in the most common case
		 * (i.e. no trigger name collisions).
		 */
		char 		alphanumeric_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
							'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
							'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
							't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
							'8', '9', '\0'};
		alphnum_len = STR_LIT_LEN(alphanumeric_table);
		namesub1 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++;
		/* If WBTEST_HELPOUT_TRIGNAMEUNIQ is defined, set alphnum_len to 1. This way, we make the maximum
		 * possible combinations for the uniqe trigger names to be 3 which is significantly lesser than
		 * the actual number of combinations (62x62 = 3844). For eg., if ^a is a global having triggers defined
		 * in 4 global directories, then the possible unique trigger names are a#1# ; a#1#A ; a#1#AA.
		 */
		GTM_WHITE_BOX_TEST(WBTEST_HELPOUT_TRIGNAMEUNIQ, alphnum_len, 1);
		mident_suffix_top = (char *)alphanumeric_table + alphnum_len;
		/* Phase 1. See if any single character can add uniqueness */
		for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top; mident_suffix_p1++)
		{
			*namesub1 = *mident_suffix_p1;
			if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
				break;
		}
		if (mident_suffix_p1 == mident_suffix_top)
		{	/* Phase 2. Phase 1 could not find uniqueness .. Find it with 2 char variations */
			namesub2 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++;
			for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top;
			     mident_suffix_p1++)
			{	/* First char loop */
				for (mident_suffix_p2 = (char *)alphanumeric_table; mident_suffix_p2 < mident_suffix_top;
				     mident_suffix_p2++)
				{	/* 2nd char loop */
					*namesub1 = *mident_suffix_p1;
					*namesub2 = *mident_suffix_p2;
					if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name))
					{
						mident_suffix_p1 = mident_suffix_top + 1;	/* Break out of both loops */
						break;
					}
				}
			}
			if (mident_suffix_p1 == mident_suffix_top)
			{	/* Phase 3: Punt */
				assert(WBTEST_HELPOUT_TRIGNAMEUNIQ == gtm_white_box_test_case_number);
				rts_error(VARLSTCNT(5) ERR_TRIGNAMEUNIQ, 3, trigdsc->rtn_desc.rt_name.len - 2,
					  trigdsc->rtn_desc.rt_name.addr, alphnum_len * alphnum_len);
			}
		}
	}
	/* Write trigger execute string out to temporary file and compile it */
	assert(MAX_XECUTE_LEN >= trigdsc->xecute_str.str.len);
	rc = SNPRINTF(rtnname_template, GTM_PATH_MAX, "%s/trgtmpXXXXXX", DEFAULT_GTM_TMP);
	assert(0 < rc);					/* Note rc is return code aka length - we expect a non-zero length */
	assert(GTM_PATH_MAX >= rc);
	/* The mkstemp() routine is known to bogus-fail for no apparent reason at all especially on AIX 6.1. In the event
	 * this shortcoming plagues other platforms as well, we add a low-cost retry wrapper.
	 */
	retry = MAX_MKSTEMP_RETRIES;
	do
	{
		strcpy(rtnname, rtnname_template);
		rtnfd = mkstemp(rtnname);
	} while ((-1 == rtnfd) && (EEXIST == errno) && (0 < --retry));
	if (-1 == rtnfd)
	{
		save_errno = errno;
		assert(FALSE);
		rts_error(VARLSTCNT(12) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("mkstemp()"), CALLFROM,
			  ERR_TEXT, 2, RTS_ERROR_TEXT(rtnname), save_errno);
	}
	assert(0 < rtnfd);	/* Verify file descriptor */
	rc = 0;
#	ifdef GEN_TRIGCOMPFAIL_ERROR
	{	/* Used ONLY to generate an error in a trigger compile by adding some junk in a previous line */
		DOWRITERC(rtnfd, ERROR_CAUSING_JUNK, strlen(ERROR_CAUSING_JUNK), rc); /* BYPASSOK */
		if (0 != rc)
		{
			UNLINK(rtnname);
			rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
		}
	}
#	endif
	DOWRITERC(rtnfd, trigdsc->xecute_str.str.addr, trigdsc->xecute_str.str.len, rc);
	if (0 != rc)
	{
		UNLINK(rtnname);
		rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
	}
	if (NULL == memchr(trigdsc->xecute_str.str.addr, '\n', trigdsc->xecute_str.str.len))
	{
		DOWRITERC(rtnfd, NEWLINE, strlen(NEWLINE), rc);			/* BYPASSOK */
		if (0 != rc)
		{
			UNLINK(rtnname);
			rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc);
		}
	}
	CLOSEFILE(rtnfd, rc);
	if (0 != rc)
	{
		UNLINK(rtnname);
		rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, rc);
	}
	assert(MAX_MIDENT_LEN > trigdsc->rtn_desc.rt_name.len);
	zcomp_parms_ptr = zcomp_parms;
	lenrtnname = STRLEN(rtnname);
	MEMCPY_LIT(zcomp_parms_ptr, NAMEOFRTN_PARM);
	zcomp_parms_ptr += STRLEN(NAMEOFRTN_PARM);
	memcpy(zcomp_parms_ptr, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len);
	zcomp_parms_ptr += trigdsc->rtn_desc.rt_name.len;
	MEMCPY_LIT(zcomp_parms_ptr, OBJECT_PARM);
	zcomp_parms_ptr += STRLEN(OBJECT_PARM);
	strcpy(objname, rtnname);		/* Make copy of rtnname to become object name */
	strcat(objname, OBJECT_FTYPE);		/* Turn into object file reference */
	lenobjname = lenrtnname + STRLEN(OBJECT_FTYPE);
	memcpy(zcomp_parms_ptr, objname, lenobjname);
	zcomp_parms_ptr += lenobjname;
	*zcomp_parms_ptr++ = ' ';
	memcpy(zcomp_parms_ptr, rtnname, lenrtnname);
	zcomp_parms_ptr += lenrtnname;
	*zcomp_parms_ptr = '\0';		/* Null tail */
	len = INTCAST(zcomp_parms_ptr - zcomp_parms);
	assert((SIZEOF(zcomp_parms) - 1) > len);	/* Verify no overflow */
	zcompprm.mvtype = MV_STR;
	zcompprm.str.addr = zcomp_parms;
	zcompprm.str.len = len;
	/* Backup dollar_zsource so trigger doesn't show */
	PUSH_MV_STENT(MVST_MSAV);
	mv_chain->mv_st_cont.mvs_msav.v = dollar_zsource;
	mv_chain->mv_st_cont.mvs_msav.addr = &dollar_zsource;
	TREF(trigger_compile) = TRUE;		/* Set flag so compiler knows this is a special trigger compile */
	op_zcompile(&zcompprm, FALSE);	/* Compile but don't require a .m file extension */
	TREF(trigger_compile) = FALSE;	/* compile_source_file() establishes handler so always returns */
	if (0 != TREF(dollar_zcstatus))
	{	/* Someone err'd.. */
		run_time = gtm_trigger_comp_prev_run_time;
		REVERT;
		UNLINK(objname);	/* Remove files before return error */
		UNLINK(rtnname);
		return ERR_TRIGCOMPFAIL;
	}
	if (dolink)
	{	/* Link is optional as MUPIP TRIGGER doesn't need link */
		zlfile.mvtype = MV_STR;
		zlfile.str.addr = objname;
		zlfile.str.len = lenobjname;
		/* Specifying literal_null for a second arg (as opposed to NULL or 0) allows us to specify
		 * linking the object file (no compilation or looking for source). The 2nd arg is parms for
		 * recompilation and is non-null in an explicit zlink which we need to emulate.
		 */
#		ifdef GEN_TRIGLINKFAIL_ERROR
		UNLINK(objname);				/* delete object before it can be used */
#		endif
		op_zlink(&zlfile, (mval *)&literal_null);	/* need cast due to "extern const" attributes */
		/* No return here if link fails for some reason */
		trigdsc->rtn_desc.rt_adr = find_rtn_hdr(&trigdsc->rtn_desc.rt_name);
		if (NULL == trigdsc->rtn_desc.rt_adr)
			GTMASSERT;	/* Can't find routine we just put there? Catastrophic if happens */
		/* Replace the randomly generated source name with the constant "GTM Trigger" */
		trigdsc->rtn_desc.rt_adr->src_full_name.addr = GTM_TRIGGER_SOURCE_NAME;
		trigdsc->rtn_desc.rt_adr->src_full_name.len = STRLEN(GTM_TRIGGER_SOURCE_NAME);
		trigdsc->rtn_desc.rt_adr->trigr_handle = trigdsc;       /* Back pointer to trig def */
	}
	if (MVST_MSAV == mv_chain->mv_st_type && &dollar_zsource == mv_chain->mv_st_cont.mvs_msav.addr)
	{       /* Top mv_stent is one we pushed on there - restore dollar_zsource and get rid of it */
		dollar_zsource = mv_chain->mv_st_cont.mvs_msav.v;
		POP_MV_STENT();
	} else
		assert(FALSE); 	/* This mv_stent should be the one we just pushed */
	/* Remove temporary files created */
	UNLINK(objname);	/* Delete the object file first since rtnname is the unique key */
	UNLINK(rtnname);	/* Delete the source file */
	run_time = gtm_trigger_comp_prev_run_time;
	REVERT;
	return 0;
}