163d1a8abSmrg /* DWARF2 EH unwinding support for IA64 VMS.
2*ec02198aSmrg    Copyright (C) 2005-2020 Free Software Foundation, Inc.
363d1a8abSmrg 
463d1a8abSmrg    This file is part of GCC.
563d1a8abSmrg 
663d1a8abSmrg    GCC is free software; you can redistribute it and/or modify it
763d1a8abSmrg    under the terms of the GNU General Public License as published
863d1a8abSmrg    by the Free Software Foundation; either version 3, or (at your
963d1a8abSmrg    option) any later version.
1063d1a8abSmrg 
1163d1a8abSmrg    GCC is distributed in the hope that it will be useful, but WITHOUT
1263d1a8abSmrg    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
1363d1a8abSmrg    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
1463d1a8abSmrg    License for more details.
1563d1a8abSmrg 
1663d1a8abSmrg    Under Section 7 of GPL version 3, you are granted additional
1763d1a8abSmrg    permissions described in the GCC Runtime Library Exception, version
1863d1a8abSmrg    3.1, as published by the Free Software Foundation.
1963d1a8abSmrg 
2063d1a8abSmrg    You should have received a copy of the GNU General Public License and
2163d1a8abSmrg    a copy of the GCC Runtime Library Exception along with this program;
2263d1a8abSmrg    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
2363d1a8abSmrg    <http://www.gnu.org/licenses/>.  */
2463d1a8abSmrg 
2563d1a8abSmrg #define __NEW_STARLET
2663d1a8abSmrg #include <libicb.h>
2763d1a8abSmrg #include <chfdef.h>
2863d1a8abSmrg #include <lib_c/chfctxdef.h>
2963d1a8abSmrg #include <lib_c/intstkdef.h>
3063d1a8abSmrg 
3163d1a8abSmrg #include <stdio.h>
3263d1a8abSmrg #include <string.h>
3363d1a8abSmrg 
3463d1a8abSmrg #define UNW_IVMS_MODE(HEADER) (((HEADER) >> 44) & 0x3L)
3563d1a8abSmrg #define MD_UNW_COMPATIBLE_PERSONALITY_P(HEADER) (!UNW_IVMS_MODE (HEADER))
3663d1a8abSmrg 
3763d1a8abSmrg #define DYN$C_SSENTRY 66
3863d1a8abSmrg /* ??? would rather get the proper header file.  */
3963d1a8abSmrg 
4063d1a8abSmrg #define MD_FALLBACK_FRAME_STATE_FOR ia64_vms_fallback_frame_state
4163d1a8abSmrg 
4263d1a8abSmrg extern INVO_CONTEXT_BLK * LIB$I64_CREATE_INVO_CONTEXT (void);
4363d1a8abSmrg 
4463d1a8abSmrg extern int LIB$I64_IS_EXC_DISPATCH_FRAME (void *);
4563d1a8abSmrg extern int LIB$I64_IS_AST_DISPATCH_FRAME (void *);
4663d1a8abSmrg 
4763d1a8abSmrg extern int LIB$I64_INIT_INVO_CONTEXT (INVO_CONTEXT_BLK *, int, int);
4863d1a8abSmrg extern int LIB$I64_GET_CURR_INVO_CONTEXT (INVO_CONTEXT_BLK *);
4963d1a8abSmrg extern int LIB$I64_GET_PREV_INVO_CONTEXT (INVO_CONTEXT_BLK *);
5063d1a8abSmrg 
5163d1a8abSmrg typedef unsigned int uint;
5263d1a8abSmrg typedef unsigned __int64 uw_reg;
5363d1a8abSmrg typedef uw_reg * uw_loc;
5463d1a8abSmrg 
5563d1a8abSmrg typedef char fp_reg[16];
5663d1a8abSmrg 
5763d1a8abSmrg #define DENOTES_VMS_DISPATCHER_FRAME(icb) \
5863d1a8abSmrg (LIB$I64_IS_EXC_DISPATCH_FRAME (&(icb)->libicb$ih_pc))
5963d1a8abSmrg 
6063d1a8abSmrg #define DENOTES_BOTTOM_OF_STACK(icb) ((icb)->libicb$v_bottom_of_stack)
6163d1a8abSmrg 
6263d1a8abSmrg #define FAIL_IF(COND) \
6363d1a8abSmrg    do { if (COND) { context->rp = 0; return _URC_END_OF_STACK; } } while (0)
6463d1a8abSmrg /* Clearing context->rp is required to prevent the ia64 gcc unwinder from
6563d1a8abSmrg    attempting to keep on walking the call chain.  */
6663d1a8abSmrg 
6763d1a8abSmrg static int
ia64_vms_fallback_frame_state(struct _Unwind_Context * context,_Unwind_FrameState * fs)6863d1a8abSmrg ia64_vms_fallback_frame_state (struct _Unwind_Context *context,
6963d1a8abSmrg 			       _Unwind_FrameState *fs)
7063d1a8abSmrg {
7163d1a8abSmrg   int i, status;
7263d1a8abSmrg 
7363d1a8abSmrg   INVO_CONTEXT_BLK local_icb;
7463d1a8abSmrg   INVO_CONTEXT_BLK *icb = &local_icb;
7563d1a8abSmrg 
7663d1a8abSmrg   CHFCTX * chfctx;
7763d1a8abSmrg   CHF$MECH_ARRAY * chfmech;
7863d1a8abSmrg   CHF64$SIGNAL_ARRAY *chfsig64;
7963d1a8abSmrg   INTSTK * intstk;
8063d1a8abSmrg 
8163d1a8abSmrg   static int eh_debug = -1;
8263d1a8abSmrg   int try_bs_copy = 0;
8363d1a8abSmrg   /* Non zero to attempt copy of alternate backing store contents for
8463d1a8abSmrg      dirty partition in interrupted context. ??? Alpha code, only activated
8563d1a8abSmrg      on specific request via specific bit in EH_DEBUG.  */
8663d1a8abSmrg 
8763d1a8abSmrg   if (eh_debug == -1)
8863d1a8abSmrg     {
8963d1a8abSmrg       char * EH_DEBUG = getenv ("EH_DEBUG");
9063d1a8abSmrg       const uint try_bs_copy_mask = (1 << 16);
9163d1a8abSmrg 
9263d1a8abSmrg       eh_debug = EH_DEBUG ? atoi (EH_DEBUG) : 0;
9363d1a8abSmrg 
9463d1a8abSmrg       /* Fetch and clear the try_bs_copy bit.  */
9563d1a8abSmrg       try_bs_copy = (uint)eh_debug & try_bs_copy_mask;
9663d1a8abSmrg       eh_debug &= ~try_bs_copy_mask;
9763d1a8abSmrg     }
9863d1a8abSmrg 
9963d1a8abSmrg   /* We're called to attempt unwinding through a frame for which no unwind
10063d1a8abSmrg      info is available, typical of an operating system exception dispatcher
10163d1a8abSmrg      frame.  The code below knows how to handle this case, and only this one,
10263d1a8abSmrg      returning a failure code if it finds it is not in this situation.
10363d1a8abSmrg 
10463d1a8abSmrg      Note that we're called from deep down in the exception propagation call
10563d1a8abSmrg      chain, possibly below an exception dispatcher but for a frame above it
10663d1a8abSmrg      like some os entry point.  */
10763d1a8abSmrg 
10863d1a8abSmrg   if (eh_debug)
10963d1a8abSmrg     printf ("FALLBACK - ctxt->rp=0x%lx, sp=0x%lx, psp=0x%lx, bsp=0x%lx\n",
11063d1a8abSmrg 	    context->rp, context->sp, context->psp, context->bsp);
11163d1a8abSmrg 
11263d1a8abSmrg   /* Step 0 :
11363d1a8abSmrg      -------------------------------------------------------------------------
11463d1a8abSmrg      VMS-unwind up until we reach a VMS dispatcher frame corresponding to the
11563d1a8abSmrg      context we are trying to unwind through. Fail if get past this context or
11663d1a8abSmrg      if we reach the bottom of stack along the way.
11763d1a8abSmrg      -------------------------------------------------------------------------
11863d1a8abSmrg   */
11963d1a8abSmrg 
12063d1a8abSmrg   status = LIB$I64_INIT_INVO_CONTEXT (icb, LIBICB$K_INVO_CONTEXT_VERSION, 0);
12163d1a8abSmrg   FAIL_IF (status == 0);
12263d1a8abSmrg 
12363d1a8abSmrg   status = LIB$I64_GET_CURR_INVO_CONTEXT (icb);
12463d1a8abSmrg 
12563d1a8abSmrg   /* Beware: we might be unwinding through nested condition handlers, so the
12663d1a8abSmrg      dispatcher frame we seek might not be the first one on the way up.  Loop
12763d1a8abSmrg      thus.  */
12863d1a8abSmrg   do {
12963d1a8abSmrg 
13063d1a8abSmrg     /* Seek the next dispatcher frame up the "current" point.  Stop if we
13163d1a8abSmrg        either get past the target context or hit the bottom-of-stack along
13263d1a8abSmrg        the way.  */
13363d1a8abSmrg     status = LIB$I64_GET_PREV_INVO_CONTEXT (icb);
13463d1a8abSmrg     FAIL_IF (status == 0);
13563d1a8abSmrg     FAIL_IF ((uw_reg)icb->libicb$ih_sp > (uw_reg)context->psp
13663d1a8abSmrg 	     || DENOTES_BOTTOM_OF_STACK (icb));
13763d1a8abSmrg 
13863d1a8abSmrg     if (eh_debug)
13963d1a8abSmrg       printf ("frame%s sp @ 0x%llx, pc @ 0x%llx bsp=0x%llx\n",
14063d1a8abSmrg 	      DENOTES_VMS_DISPATCHER_FRAME (icb) ? " (dispatcher)" : "",
14163d1a8abSmrg 	      icb->libicb$ih_sp, icb->libicb$ih_pc, icb->libicb$ih_bsp);
14263d1a8abSmrg 
14363d1a8abSmrg     /* Continue until the target frame is found.  */
14463d1a8abSmrg   } while ((uw_reg)icb->libicb$ih_bsp != (uw_reg)context->bsp);
14563d1a8abSmrg 
14663d1a8abSmrg   /* If this is not a dispatcher frame, this is certainly a frame for a leaf
14763d1a8abSmrg      subprogram.  Use default unwind information.  */
14863d1a8abSmrg   if (! DENOTES_VMS_DISPATCHER_FRAME (icb))
14963d1a8abSmrg     return _URC_END_OF_STACK;
15063d1a8abSmrg 
15163d1a8abSmrg   /* At this point, we know we are really trying to unwind past an exception
15263d1a8abSmrg      dispatcher frame, and have it described in ICB.  Proceed.  */
15363d1a8abSmrg 
15463d1a8abSmrg   /* Step 1 :
15563d1a8abSmrg      ------------------------------------------------------------------------
15663d1a8abSmrg      We have the VMS dispatcher frame ICB handy and know we are trying to
15763d1a8abSmrg      unwind past it.  Fetch pointers to useful datastructures from there, then
15863d1a8abSmrg      unwind one step further up to the interrupted user context from which
15963d1a8abSmrg      some required values will be easily accessible.
16063d1a8abSmrg      ------------------------------------------------------------------------
16163d1a8abSmrg   */
16263d1a8abSmrg 
16363d1a8abSmrg   chfctx = icb->libicb$ph_chfctx_addr;
16463d1a8abSmrg   FAIL_IF (chfctx == 0);
16563d1a8abSmrg 
16663d1a8abSmrg   chfmech = (CHF$MECH_ARRAY *)chfctx->chfctx$q_mcharglst;
16763d1a8abSmrg   FAIL_IF (chfmech == 0);
16863d1a8abSmrg 
16963d1a8abSmrg   chfsig64 = (CHF64$SIGNAL_ARRAY *)chfmech->chf$ph_mch_sig64_addr;
17063d1a8abSmrg   FAIL_IF (chfsig64 == 0);
17163d1a8abSmrg 
17263d1a8abSmrg   intstk = (INTSTK *)chfmech->chf$q_mch_esf_addr;
17363d1a8abSmrg   FAIL_IF (intstk == 0 || intstk->intstk$b_subtype == DYN$C_SSENTRY);
17463d1a8abSmrg 
17563d1a8abSmrg   status = LIB$I64_GET_PREV_INVO_CONTEXT (icb);
17663d1a8abSmrg   FAIL_IF (status == 0);
17763d1a8abSmrg 
17863d1a8abSmrg   if (eh_debug)
17963d1a8abSmrg     printf ("User frame, "
18063d1a8abSmrg 	    "chfmech @ 0x%p, chfsig64 @ 0x%p, intstk @ 0x%p\n",
18163d1a8abSmrg 	    chfmech, chfsig64, intstk);
18263d1a8abSmrg 
18363d1a8abSmrg   /* Step 2 :
18463d1a8abSmrg      ------------------------------------------------------------------------
18563d1a8abSmrg      Point the GCC context locations/values required for further unwinding at
18663d1a8abSmrg      their corresponding locations/values in the datastructures at hand.
18763d1a8abSmrg      ------------------------------------------------------------------------
18863d1a8abSmrg   */
18963d1a8abSmrg 
19063d1a8abSmrg   /* Static General Register locations, including scratch registers in case
19163d1a8abSmrg      the unwinder needs to refer to a value stored in one of them.  */
19263d1a8abSmrg   {
19363d1a8abSmrg     uw_reg * ctxregs = (uw_reg *)&intstk->intstk$q_regbase;
19463d1a8abSmrg 
19563d1a8abSmrg     for (i = 2; i <= 3; i++)
19663d1a8abSmrg       context->ireg[i - 2].loc = (uw_loc)&ctxregs[i];
19763d1a8abSmrg     for (i = 8; i <= 11; i++)
19863d1a8abSmrg       context->ireg[i - 2].loc = (uw_loc)&ctxregs[i];
19963d1a8abSmrg     for (i = 14; i <= 31; i++)
20063d1a8abSmrg       context->ireg[i - 2].loc = (uw_loc)&ctxregs[i];
20163d1a8abSmrg   }
20263d1a8abSmrg 
20363d1a8abSmrg   /* Static Floating Point Register locations, as available from the
20463d1a8abSmrg      mechargs array, which happens to include all the to be preserved
20563d1a8abSmrg      ones + others.  */
20663d1a8abSmrg   {
20763d1a8abSmrg     fp_reg * ctxregs;
20863d1a8abSmrg 
20963d1a8abSmrg     ctxregs = (fp_reg *)&chfmech->chf$fh_mch_savf2;
21063d1a8abSmrg     for (i = 2; i <= 5 ; i++)
21163d1a8abSmrg       context->fr_loc[i - 2] = (uw_loc)&ctxregs[i - 2];
21263d1a8abSmrg 
21363d1a8abSmrg     ctxregs = (fp_reg *)&chfmech->chf$fh_mch_savf12;
21463d1a8abSmrg     for (i = 12; i <= 31 ; i++)
21563d1a8abSmrg       context->fr_loc[i - 2] = (uw_loc)&ctxregs[i - 12];
21663d1a8abSmrg   }
21763d1a8abSmrg 
21863d1a8abSmrg   /* Relevant application register locations.  */
21963d1a8abSmrg 
22063d1a8abSmrg   context->fpsr_loc = (uw_loc)&intstk->intstk$q_fpsr;
22163d1a8abSmrg   context->lc_loc   = (uw_loc)&intstk->intstk$q_lc;
22263d1a8abSmrg   context->unat_loc = (uw_loc)&intstk->intstk$q_unat;
22363d1a8abSmrg 
22463d1a8abSmrg   /* Branch register locations.  */
22563d1a8abSmrg 
22663d1a8abSmrg   {
22763d1a8abSmrg     uw_reg * ctxregs = (uw_reg *)&intstk->intstk$q_b0;
22863d1a8abSmrg 
22963d1a8abSmrg     for (i = 0; i < 8; i++)
23063d1a8abSmrg       context->br_loc[i] = (uw_loc)&ctxregs[i];
23163d1a8abSmrg   }
23263d1a8abSmrg 
23363d1a8abSmrg   /* Necessary register values.  */
23463d1a8abSmrg 
23563d1a8abSmrg   /* ??? Still unclear if we need to account for possible flushes to an
23663d1a8abSmrg      alternate backing store (maybe the unwinding performed above did the
23763d1a8abSmrg      trick already) and how this would be handled.  Blind alpha tentative
23863d1a8abSmrg      below for experimentation purposes in malfunctioning cases.  */
23963d1a8abSmrg   {
24063d1a8abSmrg     uw_reg q_bsp      = (uw_reg) intstk->intstk$q_bsp;
24163d1a8abSmrg     uw_reg q_bspstore = (uw_reg) intstk->intstk$q_bspstore;
24263d1a8abSmrg     uw_reg q_bspbase  = (uw_reg) intstk->intstk$q_bspbase;
24363d1a8abSmrg     uw_reg ih_bspbase = (uw_reg) icb->libicb$ih_bspbase;
24463d1a8abSmrg 
24563d1a8abSmrg     if (eh_debug)
24663d1a8abSmrg       printf ("q_bspstore = 0x%lx, q_bsp = 0x%lx, q_bspbase = 0x%lx\n"
24763d1a8abSmrg 	      "ih_bspbase = 0x%lx\n",
24863d1a8abSmrg 	      q_bspstore, q_bsp, q_bspbase, ih_bspbase);
24963d1a8abSmrg 
25063d1a8abSmrg     /* We witness many situations where q_bspbase is set while ih_bspbase is
25163d1a8abSmrg        null, and every attempt made with q_bspbase badly failed while doing
25263d1a8abSmrg        nothing resulted in proper behavior.  */
25363d1a8abSmrg     if (q_bspstore < q_bsp && ih_bspbase && try_bs_copy)
25463d1a8abSmrg       {
25563d1a8abSmrg 	uw_reg dirty_size = q_bsp - q_bspstore;
25663d1a8abSmrg 	uw_reg q_rnat = (uw_reg) intstk->intstk$q_rnat;
25763d1a8abSmrg 
25863d1a8abSmrg 	if (eh_debug)
25963d1a8abSmrg 	  printf ("Attempting an alternate backing store copy ...\n");
26063d1a8abSmrg 
26163d1a8abSmrg 	ia64_copy_rbs
26263d1a8abSmrg 	  (context, q_bspstore, ih_bspbase, dirty_size, q_rnat);
26363d1a8abSmrg 	/* Not clear if these are the proper arguments here.  This is what
26463d1a8abSmrg 	   looked the closest to what is performed in the Linux case.  */
26563d1a8abSmrg       }
26663d1a8abSmrg 
26763d1a8abSmrg   }
26863d1a8abSmrg 
26963d1a8abSmrg   context->bsp = (uw_reg)intstk->intstk$q_bsp;
27063d1a8abSmrg   fs->no_reg_stack_frame = 1;
27163d1a8abSmrg 
27263d1a8abSmrg   context->pr  = (uw_reg)intstk->intstk$q_preds;
27363d1a8abSmrg   context->gp  = (uw_reg)intstk->intstk$q_gp;
27463d1a8abSmrg 
27563d1a8abSmrg   /* We're directly setting up the "context" for a VMS exception handler.
27663d1a8abSmrg      The "previous SP" for it is the SP upon the handler's entry, that is
27763d1a8abSmrg      the SP at the condition/interruption/exception point.  */
27863d1a8abSmrg   context->psp = (uw_reg)icb->libicb$ih_sp;
27963d1a8abSmrg 
28063d1a8abSmrg   /* Previous Frame State location.  What eventually ends up in pfs_loc is
28163d1a8abSmrg      installed with ar.pfs = pfs_loc; br.ret; so setup to target intstk->q_ifs
28263d1a8abSmrg      to have the interrupted context restored and not that of its caller if
28363d1a8abSmrg      we happen to have a handler in the interrupted context itself.  */
28463d1a8abSmrg   fs->curr.reg[UNW_REG_PFS].where = UNW_WHERE_PSPREL;
28563d1a8abSmrg   fs->curr.reg[UNW_REG_PFS].val
28663d1a8abSmrg     = (uw_reg)&intstk->intstk$q_ifs - (uw_reg)context->psp;
28763d1a8abSmrg   fs->curr.reg[UNW_REG_PFS].when = -1;
28863d1a8abSmrg 
28963d1a8abSmrg   /* If we need to unwind further up, past the interrupted context, we need to
29063d1a8abSmrg      hand out the interrupted context's pfs, still.  */
29163d1a8abSmrg   context->signal_pfs_loc = (uw_loc) &intstk->intstk$q_pfs;
29263d1a8abSmrg 
29363d1a8abSmrg   /* Finally, rules for RP .  */
29463d1a8abSmrg   {
29563d1a8abSmrg     uw_reg * post_sigarray
29663d1a8abSmrg       = (uw_reg *)chfsig64 + 1 + chfsig64->chf64$l_sig_args;
29763d1a8abSmrg 
29863d1a8abSmrg     uw_reg * ih_pc_loc = post_sigarray - 2;
29963d1a8abSmrg 
30063d1a8abSmrg     fs->curr.reg[UNW_REG_RP].where = UNW_WHERE_PSPREL;
30163d1a8abSmrg     fs->curr.reg[UNW_REG_RP].val
30263d1a8abSmrg       = (uw_reg)ih_pc_loc - (uw_reg)context->psp;
30363d1a8abSmrg     fs->curr.reg[UNW_REG_RP].when = -1;
30463d1a8abSmrg   }
30563d1a8abSmrg 
30663d1a8abSmrg   return _URC_NO_REASON;
30763d1a8abSmrg }
30863d1a8abSmrg 
309