File: op_zgoto.c

package info (click to toggle)
fis-gtm 6.3-007-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 36,284 kB
  • sloc: ansic: 328,861; asm: 5,182; csh: 5,102; sh: 1,918; awk: 291; makefile: 69; sed: 13
file content (226 lines) | stat: -rw-r--r-- 8,479 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/****************************************************************
 *								*
 * Copyright (c) 2011-2017 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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 "gtm_stdio.h"
#include "gtm_string.h"

#include <rtnhdr.h>
#include "stack_frame.h"
#include "op.h"
#include "flush_jmp.h"
#include "dollar_zlevel.h"
#include "golevel.h"
#include <auto_zlink.h>
#include "error.h"
#include "gtmimagename.h"
#include "linktrc.h"
#ifdef UNIX
#include "gtm_unlink_all.h"
#endif
#ifdef GTM_TRIGGER
#include "gtm_trigger_trc.h"
#endif
#ifdef VMS
#include "pdscdef.h"
#include "proc_desc.h"
#endif

GBLREF	stack_frame	*frame_pointer;
#ifdef GTM_TRIGGER
GBLREF	boolean_t	goframes_unwound_trigger;
#endif

LITREF	gtmImageName 	gtmImageNames[];

error_def(ERR_LABELUNKNOWN);
error_def(ERR_RTNNAME);
error_def(ERR_ZGOCALLOUTIN);
error_def(ERR_ZGOTOINVLVL);
error_def(ERR_ZGOTOINVLVL2);
error_def(ERR_ZGOTOLTZERO);
error_def(ERR_ZGOTOTOOBIG);

void op_zgoto(mval *rtn_name, mval *lbl_name, int offset, int level)
{
	stack_frame	*fp, *fpprev;
	int4		curlvl;
	mval		rtnname, lblname;
	rhdtyp		*rtnhdr;
	lnr_tabent 	USHBIN_ONLY(*)*lnrptr;
	void		(*frame_func)();
	char		rtnname_buff[SIZEOF(mident_fixed)], lblname_buff[SIZEOF(mident_fixed)];
	DEBUG_ONLY(int4	dlevel;)
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	/* Validate level parm */
	curlvl = dollar_zlevel();
        if (0 > level)
	{	/* Negative level specified, means to use relative level change */
		if ((-level) > curlvl)
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZGOTOLTZERO);
		level += curlvl;	/* Compute relative desired level */
	} else
	{	/* Else level is the level we wish to achieve - compute unrolls necessary */
		if (0 > (curlvl - level))
			/* Couldn't get to the level we were trying to unwind to */
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZGOTOTOOBIG);
	}
	/* Migrate mval parm contents to private buffers since the mvals could die as we unwind things */
	MV_FORCE_STR(rtn_name);
	MV_FORCE_STR(lbl_name);
	rtnname = *rtn_name;
	lblname = *lbl_name;
	memcpy(rtnname_buff, rtnname.str.addr, rtnname.str.len);
	rtnname.str.addr = rtnname_buff;
	memcpy(lblname_buff, lblname.str.addr, lblname.str.len);
	lblname.str.addr = lblname_buff;
	DBGEHND((stderr, "op_zgoto: rtnname: %.*s  lblname: %.*s  offset: %d  level: %d\n",
		 rtnname.str.len, rtnname.str.addr, lblname.str.len, lblname.str.addr, offset, level));
	/* Validate entryref before do any unwinding */
	if (0 == rtnname.str.len)
	{	/* If no routine name given, take it from the currently running routine unless the label name
		 * is also NULL. In that case, we must be doing an indirect routine name only and the supplied name
		 * was NULL.
		 */
		if ((0 == lblname.str.len) && !offset)
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_RTNNAME);
		rtnhdr = frame_pointer->rvector;
		ARLINK_ONLY(TADR(lnk_proxy)->rtnhdr_adr = rtnhdr);
		DBGINDCOMP((stderr, "op_zgoto: routine resolved to 0x"lvaddr" (1)\n", rtnhdr));
		if (0 == level)
		{	/* If doing unlink, recall name of routine as well as will need it later */
			memcpy(rtnname_buff, rtnhdr->routine_name.addr, rtnhdr->routine_name.len);
			rtnname.str.addr = rtnname_buff;
			rtnname.str.len = rtnhdr->routine_name.len;
		}
	} else
	{
#		ifdef AUTORELINK_SUPPORTED
		op_rhdaddr(&rtnname, -1);		/* Does an autozlink if necessary */
		rtnhdr = TADR(lnk_proxy)->rtnhdr_adr;
		DBGINDCOMP((stderr, "op_zgoto: routine resolved to 0x"lvaddr" (2)\n", rtnhdr));
#		else
		rtnhdr = op_rhdaddr(&rtnname, NULL);	/* Does an autozlink if necessary */
#		endif
#		ifdef VMS
		if (PDSC_FLAGS == ((proc_desc *)rtnhdr)->flags) /* it's a procedure descriptor, not a routine header */
		{
			rtnhdr = (rhdtyp *)(((proc_desc *)rtnhdr)->code_address);
			/* Make sure this if-test is sufficient to distinguish between procedure descriptors and routine headers */
			assert(PDSC_FLAGS != ((proc_desc *)rtnhdr)->flags);
		}
#		endif
	}
	assert(NULL != rtnhdr);
#	ifdef AUTORELINK_SUPPORTED
	op_labaddr(0, &lblname, offset);
	lnrptr = &TADR(lnk_proxy)->lnr_adr;
#	else
	lnrptr = op_labaddr(rtnhdr, &lblname, offset);
#	endif
	assert(NULL != lnrptr);
#	ifdef VMS
	/* VMS does not support unlink so any level 0 request that passes earlier checks just generates an error
	 * since the base frame cannot be rewritten as a GTM frame.
	 */
	if (0 == level)
		rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZGOTOINVLVL2);
#	endif
#	ifdef GTM_TRIGGER
	if (!IS_GTM_IMAGE && (1 >= level))
		/* In MUPIP (or other utility that gets trigger support in the future), levels 0 and 1 refer to
		 * the pseudo baseframe and initial stack levels which are not rewritable (in the case where an
		 * entry ref was coded) and are not resume-able (if no entry ref were specified) so we cannot
		 * permit ZGOTOs to these levels in a utility.
		 */
		rts_error_csa(CSA_ARG(NULL) VARLSTCNT(5) ERR_ZGOTOINVLVL, 3, GTMIMAGENAMETXT(image_type), level);
#	endif
#	ifdef UNIX
	/* One last check if we are unlinking, make sure no call-in frames exist on our stack */
	if (0 == level)
	{
		for (fp = frame_pointer; NULL != fp; fp = fpprev)
		{
			fpprev = fp->old_frame_pointer;
			if (!(fp->type & SFT_COUNT))
				continue;
			if (fp->flags & SFF_CI)
				/* We have a call-in frame - cannot do unlink */
				rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZGOCALLOUTIN);
			if (NULL == fpprev)
			{	/* Next frame is some sort of base frame */
#				ifdef GTM_TRIGGER
				if (fp->type & SFT_TRIGR)
				{	/* Have a trigger baseframe, pick up stack continuation frame_pointer stored by
					 *base_frame() */
					fpprev = *(stack_frame **)(fp + 1);
					continue;
				} else
#				endif
					break;			/* Some other base frame that stops us */
			}
		}
		/* Full unlink/unwind requested. First unlink everything, then relink our target entryref */
		gtm_unlink_all();
		frame_func = new_stack_frame;
		/* Now that everything is unwound, frame_pointer is the base frame. This frame contains the same rvector pointer
		 * that the level 1 stack frame routine has so it needs to also be rewritten once we link our new "1st routine".
		 */
		assert((NULL != frame_pointer) && (NULL == frame_pointer->old_frame_pointer));
#		ifdef AUTORELINK_SUPPORTED
		op_rhdaddr(&rtnname, -1);
		rtnhdr = TADR(lnk_proxy)->rtnhdr_adr;
#		else
		rtnhdr = op_rhdaddr(&rtnname, NULL);
#		endif
		assert(NULL != rtnhdr);
		frame_pointer->rvector = rtnhdr;
#		ifdef AUTORELINK_SUPPORTED
		op_labaddr(0, &lblname, offset);
		lnrptr = &TADR(lnk_proxy)->lnr_adr;
#		else
		lnrptr = op_labaddr(rtnhdr, &lblname, offset);
#		endif
		assert(NULL != lnrptr);
	} else
#	endif	/* UNIX */
	{	/* Unwind to our target level (UNIX if 0 != level  and VMS [all]) */
		GOLEVEL(level, TRUE);	/* Unwind the trigger base frame if we run into one */
		assert(level == dollar_zlevel());
		frame_func = flush_jmp;
	}
	/* Convert current stack frame (flush_jmp) to the frame for the entry ref we want to branch to, or create a new level 1
	 * frame (new_stack_frame) after ZGOTO 0.
	 */
	USHBIN_ONLY((* frame_func)(rtnhdr, (unsigned char *)LINKAGE_ADR(rtnhdr), LINE_NUMBER_ADDR(rtnhdr, *lnrptr)));
	/* on non-shared binary calculate the transfer address to be passed to flush_jmp as follows:
	 * 	1) get the number stored at lnrptr; this is the offset to the line number entry
	 *	2) add the said offset to the address of the routine header; this is the address of line number entry
	 *	3) dereference the said address to get the line number of the actual program
	 *	4) add the said line number to the address of the routine header
	 */
	NON_USHBIN_ONLY((* frame_func)(rtnhdr, (unsigned char *)LINKAGE_ADR(rtnhdr),
		(unsigned char *)((int)rtnhdr + *(int *)((int)rtnhdr + *lnrptr))));
	DBGEHND((stderr, "op_zgoto: Resuming at frame 0x"lvaddr" with type 0x%04lx\n", frame_pointer, frame_pointer->type));
#	ifdef GTM_TRIGGER
	if (goframes_unwound_trigger)
	{	/* If goframes() called by golevel unwound a trigger base frame, we must use MUM_TSTART to unroll the
		 * C stack before invoking the return frame. Otherwise we can just return and avoid the overhead that
		 * MUM_TSTART incurs.
		 */
		MUM_TSTART;
	}
#	endif
}