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