1 /* DWARF2 EH unwinding support for IA64 VMS.
2    Copyright (C) 2005-2022 Free Software Foundation, Inc.
3 
4    This file is part of GCC.
5 
6    GCC is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published
8    by the Free Software Foundation; either version 3, or (at your
9    option) any later version.
10 
11    GCC is distributed in the hope that it will be useful, but WITHOUT
12    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14    License for more details.
15 
16    Under Section 7 of GPL version 3, you are granted additional
17    permissions described in the GCC Runtime Library Exception, version
18    3.1, as published by the Free Software Foundation.
19 
20    You should have received a copy of the GNU General Public License and
21    a copy of the GCC Runtime Library Exception along with this program;
22    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23    <http://www.gnu.org/licenses/>.  */
24 
25 #define __NEW_STARLET
26 #include <libicb.h>
27 #include <chfdef.h>
28 #include <lib_c/chfctxdef.h>
29 #include <lib_c/intstkdef.h>
30 
31 #include <stdio.h>
32 #include <string.h>
33 
34 #define UNW_IVMS_MODE(HEADER) (((HEADER) >> 44) & 0x3L)
35 #define MD_UNW_COMPATIBLE_PERSONALITY_P(HEADER) (!UNW_IVMS_MODE (HEADER))
36 
37 #define DYN$C_SSENTRY 66
38 /* ??? would rather get the proper header file.  */
39 
40 #define MD_FALLBACK_FRAME_STATE_FOR ia64_vms_fallback_frame_state
41 
42 extern INVO_CONTEXT_BLK * LIB$I64_CREATE_INVO_CONTEXT (void);
43 
44 extern int LIB$I64_IS_EXC_DISPATCH_FRAME (void *);
45 extern int LIB$I64_IS_AST_DISPATCH_FRAME (void *);
46 
47 extern int LIB$I64_INIT_INVO_CONTEXT (INVO_CONTEXT_BLK *, int, int);
48 extern int LIB$I64_GET_CURR_INVO_CONTEXT (INVO_CONTEXT_BLK *);
49 extern int LIB$I64_GET_PREV_INVO_CONTEXT (INVO_CONTEXT_BLK *);
50 
51 typedef unsigned int uint;
52 typedef unsigned __int64 uw_reg;
53 typedef uw_reg * uw_loc;
54 
55 typedef char fp_reg[16];
56 
57 #define DENOTES_VMS_DISPATCHER_FRAME(icb) \
58 (LIB$I64_IS_EXC_DISPATCH_FRAME (&(icb)->libicb$ih_pc))
59 
60 #define DENOTES_BOTTOM_OF_STACK(icb) ((icb)->libicb$v_bottom_of_stack)
61 
62 #define FAIL_IF(COND) \
63    do { if (COND) { context->rp = 0; return _URC_END_OF_STACK; } } while (0)
64 /* Clearing context->rp is required to prevent the ia64 gcc unwinder from
65    attempting to keep on walking the call chain.  */
66 
67 static int
ia64_vms_fallback_frame_state(struct _Unwind_Context * context,_Unwind_FrameState * fs)68 ia64_vms_fallback_frame_state (struct _Unwind_Context *context,
69 			       _Unwind_FrameState *fs)
70 {
71   int i, status;
72 
73   INVO_CONTEXT_BLK local_icb;
74   INVO_CONTEXT_BLK *icb = &local_icb;
75 
76   CHFCTX * chfctx;
77   CHF$MECH_ARRAY * chfmech;
78   CHF64$SIGNAL_ARRAY *chfsig64;
79   INTSTK * intstk;
80 
81   static int eh_debug = -1;
82   int try_bs_copy = 0;
83   /* Non zero to attempt copy of alternate backing store contents for
84      dirty partition in interrupted context. ??? Alpha code, only activated
85      on specific request via specific bit in EH_DEBUG.  */
86 
87   if (eh_debug == -1)
88     {
89       char * EH_DEBUG = getenv ("EH_DEBUG");
90       const uint try_bs_copy_mask = (1 << 16);
91 
92       eh_debug = EH_DEBUG ? atoi (EH_DEBUG) : 0;
93 
94       /* Fetch and clear the try_bs_copy bit.  */
95       try_bs_copy = (uint)eh_debug & try_bs_copy_mask;
96       eh_debug &= ~try_bs_copy_mask;
97     }
98 
99   /* We're called to attempt unwinding through a frame for which no unwind
100      info is available, typical of an operating system exception dispatcher
101      frame.  The code below knows how to handle this case, and only this one,
102      returning a failure code if it finds it is not in this situation.
103 
104      Note that we're called from deep down in the exception propagation call
105      chain, possibly below an exception dispatcher but for a frame above it
106      like some os entry point.  */
107 
108   if (eh_debug)
109     printf ("FALLBACK - ctxt->rp=0x%lx, sp=0x%lx, psp=0x%lx, bsp=0x%lx\n",
110 	    context->rp, context->sp, context->psp, context->bsp);
111 
112   /* Step 0 :
113      -------------------------------------------------------------------------
114      VMS-unwind up until we reach a VMS dispatcher frame corresponding to the
115      context we are trying to unwind through. Fail if get past this context or
116      if we reach the bottom of stack along the way.
117      -------------------------------------------------------------------------
118   */
119 
120   status = LIB$I64_INIT_INVO_CONTEXT (icb, LIBICB$K_INVO_CONTEXT_VERSION, 0);
121   FAIL_IF (status == 0);
122 
123   status = LIB$I64_GET_CURR_INVO_CONTEXT (icb);
124 
125   /* Beware: we might be unwinding through nested condition handlers, so the
126      dispatcher frame we seek might not be the first one on the way up.  Loop
127      thus.  */
128   do {
129 
130     /* Seek the next dispatcher frame up the "current" point.  Stop if we
131        either get past the target context or hit the bottom-of-stack along
132        the way.  */
133     status = LIB$I64_GET_PREV_INVO_CONTEXT (icb);
134     FAIL_IF (status == 0);
135     FAIL_IF ((uw_reg)icb->libicb$ih_sp > (uw_reg)context->psp
136 	     || DENOTES_BOTTOM_OF_STACK (icb));
137 
138     if (eh_debug)
139       printf ("frame%s sp @ 0x%llx, pc @ 0x%llx bsp=0x%llx\n",
140 	      DENOTES_VMS_DISPATCHER_FRAME (icb) ? " (dispatcher)" : "",
141 	      icb->libicb$ih_sp, icb->libicb$ih_pc, icb->libicb$ih_bsp);
142 
143     /* Continue until the target frame is found.  */
144   } while ((uw_reg)icb->libicb$ih_bsp != (uw_reg)context->bsp);
145 
146   /* If this is not a dispatcher frame, this is certainly a frame for a leaf
147      subprogram.  Use default unwind information.  */
148   if (! DENOTES_VMS_DISPATCHER_FRAME (icb))
149     return _URC_END_OF_STACK;
150 
151   /* At this point, we know we are really trying to unwind past an exception
152      dispatcher frame, and have it described in ICB.  Proceed.  */
153 
154   /* Step 1 :
155      ------------------------------------------------------------------------
156      We have the VMS dispatcher frame ICB handy and know we are trying to
157      unwind past it.  Fetch pointers to useful datastructures from there, then
158      unwind one step further up to the interrupted user context from which
159      some required values will be easily accessible.
160      ------------------------------------------------------------------------
161   */
162 
163   chfctx = icb->libicb$ph_chfctx_addr;
164   FAIL_IF (chfctx == 0);
165 
166   chfmech = (CHF$MECH_ARRAY *)chfctx->chfctx$q_mcharglst;
167   FAIL_IF (chfmech == 0);
168 
169   chfsig64 = (CHF64$SIGNAL_ARRAY *)chfmech->chf$ph_mch_sig64_addr;
170   FAIL_IF (chfsig64 == 0);
171 
172   intstk = (INTSTK *)chfmech->chf$q_mch_esf_addr;
173   FAIL_IF (intstk == 0 || intstk->intstk$b_subtype == DYN$C_SSENTRY);
174 
175   status = LIB$I64_GET_PREV_INVO_CONTEXT (icb);
176   FAIL_IF (status == 0);
177 
178   if (eh_debug)
179     printf ("User frame, "
180 	    "chfmech @ 0x%p, chfsig64 @ 0x%p, intstk @ 0x%p\n",
181 	    chfmech, chfsig64, intstk);
182 
183   /* Step 2 :
184      ------------------------------------------------------------------------
185      Point the GCC context locations/values required for further unwinding at
186      their corresponding locations/values in the datastructures at hand.
187      ------------------------------------------------------------------------
188   */
189 
190   /* Static General Register locations, including scratch registers in case
191      the unwinder needs to refer to a value stored in one of them.  */
192   {
193     uw_reg * ctxregs = (uw_reg *)&intstk->intstk$q_regbase;
194 
195     for (i = 2; i <= 3; i++)
196       context->ireg[i - 2].loc = (uw_loc)&ctxregs[i];
197     for (i = 8; i <= 11; i++)
198       context->ireg[i - 2].loc = (uw_loc)&ctxregs[i];
199     for (i = 14; i <= 31; i++)
200       context->ireg[i - 2].loc = (uw_loc)&ctxregs[i];
201   }
202 
203   /* Static Floating Point Register locations, as available from the
204      mechargs array, which happens to include all the to be preserved
205      ones + others.  */
206   {
207     fp_reg * ctxregs;
208 
209     ctxregs = (fp_reg *)&chfmech->chf$fh_mch_savf2;
210     for (i = 2; i <= 5 ; i++)
211       context->fr_loc[i - 2] = (uw_loc)&ctxregs[i - 2];
212 
213     ctxregs = (fp_reg *)&chfmech->chf$fh_mch_savf12;
214     for (i = 12; i <= 31 ; i++)
215       context->fr_loc[i - 2] = (uw_loc)&ctxregs[i - 12];
216   }
217 
218   /* Relevant application register locations.  */
219 
220   context->fpsr_loc = (uw_loc)&intstk->intstk$q_fpsr;
221   context->lc_loc   = (uw_loc)&intstk->intstk$q_lc;
222   context->unat_loc = (uw_loc)&intstk->intstk$q_unat;
223 
224   /* Branch register locations.  */
225 
226   {
227     uw_reg * ctxregs = (uw_reg *)&intstk->intstk$q_b0;
228 
229     for (i = 0; i < 8; i++)
230       context->br_loc[i] = (uw_loc)&ctxregs[i];
231   }
232 
233   /* Necessary register values.  */
234 
235   /* ??? Still unclear if we need to account for possible flushes to an
236      alternate backing store (maybe the unwinding performed above did the
237      trick already) and how this would be handled.  Blind alpha tentative
238      below for experimentation purposes in malfunctioning cases.  */
239   {
240     uw_reg q_bsp      = (uw_reg) intstk->intstk$q_bsp;
241     uw_reg q_bspstore = (uw_reg) intstk->intstk$q_bspstore;
242     uw_reg q_bspbase  = (uw_reg) intstk->intstk$q_bspbase;
243     uw_reg ih_bspbase = (uw_reg) icb->libicb$ih_bspbase;
244 
245     if (eh_debug)
246       printf ("q_bspstore = 0x%lx, q_bsp = 0x%lx, q_bspbase = 0x%lx\n"
247 	      "ih_bspbase = 0x%lx\n",
248 	      q_bspstore, q_bsp, q_bspbase, ih_bspbase);
249 
250     /* We witness many situations where q_bspbase is set while ih_bspbase is
251        null, and every attempt made with q_bspbase badly failed while doing
252        nothing resulted in proper behavior.  */
253     if (q_bspstore < q_bsp && ih_bspbase && try_bs_copy)
254       {
255 	uw_reg dirty_size = q_bsp - q_bspstore;
256 	uw_reg q_rnat = (uw_reg) intstk->intstk$q_rnat;
257 
258 	if (eh_debug)
259 	  printf ("Attempting an alternate backing store copy ...\n");
260 
261 	ia64_copy_rbs
262 	  (context, q_bspstore, ih_bspbase, dirty_size, q_rnat);
263 	/* Not clear if these are the proper arguments here.  This is what
264 	   looked the closest to what is performed in the Linux case.  */
265       }
266 
267   }
268 
269   context->bsp = (uw_reg)intstk->intstk$q_bsp;
270   fs->no_reg_stack_frame = 1;
271 
272   context->pr  = (uw_reg)intstk->intstk$q_preds;
273   context->gp  = (uw_reg)intstk->intstk$q_gp;
274 
275   /* We're directly setting up the "context" for a VMS exception handler.
276      The "previous SP" for it is the SP upon the handler's entry, that is
277      the SP at the condition/interruption/exception point.  */
278   context->psp = (uw_reg)icb->libicb$ih_sp;
279 
280   /* Previous Frame State location.  What eventually ends up in pfs_loc is
281      installed with ar.pfs = pfs_loc; br.ret; so setup to target intstk->q_ifs
282      to have the interrupted context restored and not that of its caller if
283      we happen to have a handler in the interrupted context itself.  */
284   fs->curr.reg[UNW_REG_PFS].where = UNW_WHERE_PSPREL;
285   fs->curr.reg[UNW_REG_PFS].val
286     = (uw_reg)&intstk->intstk$q_ifs - (uw_reg)context->psp;
287   fs->curr.reg[UNW_REG_PFS].when = -1;
288 
289   /* If we need to unwind further up, past the interrupted context, we need to
290      hand out the interrupted context's pfs, still.  */
291   context->signal_pfs_loc = (uw_loc) &intstk->intstk$q_pfs;
292 
293   /* Finally, rules for RP .  */
294   {
295     uw_reg * post_sigarray
296       = (uw_reg *)chfsig64 + 1 + chfsig64->chf64$l_sig_args;
297 
298     uw_reg * ih_pc_loc = post_sigarray - 2;
299 
300     fs->curr.reg[UNW_REG_RP].where = UNW_WHERE_PSPREL;
301     fs->curr.reg[UNW_REG_RP].val
302       = (uw_reg)ih_pc_loc - (uw_reg)context->psp;
303     fs->curr.reg[UNW_REG_RP].when = -1;
304   }
305 
306   return _URC_NO_REASON;
307 }
308 
309