]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgcc/config/ia64/vms-unwind.h
Update copyright years.
[thirdparty/gcc.git] / libgcc / config / ia64 / vms-unwind.h
CommitLineData
f51dfe4d 1/* DWARF2 EH unwinding support for IA64 VMS.
f1717362 2 Copyright (C) 2005-2016 Free Software Foundation, Inc.
f51dfe4d 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
6aadb6e2 25#define __NEW_STARLET
26#include <libicb.h>
27#include <chfdef.h>
28#include <lib_c/chfctxdef.h>
29#include <lib_c/intstkdef.h>
f51dfe4d 30
31#include <stdio.h>
32#include <string.h>
33
e59be7e3 34#define UNW_IVMS_MODE(HEADER) (((HEADER) >> 44) & 0x3L)
35#define MD_UNW_COMPATIBLE_PERSONALITY_P(HEADER) (!UNW_IVMS_MODE (HEADER))
36
f51dfe4d 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
42extern INVO_CONTEXT_BLK * LIB$I64_CREATE_INVO_CONTEXT (void);
43
44extern int LIB$I64_IS_EXC_DISPATCH_FRAME (void *);
45extern int LIB$I64_IS_AST_DISPATCH_FRAME (void *);
46
47extern int LIB$I64_INIT_INVO_CONTEXT (INVO_CONTEXT_BLK *, int, int);
48extern int LIB$I64_GET_CURR_INVO_CONTEXT (INVO_CONTEXT_BLK *);
49extern int LIB$I64_GET_PREV_INVO_CONTEXT (INVO_CONTEXT_BLK *);
50
f51dfe4d 51typedef unsigned int uint;
46da3ce5 52typedef unsigned __int64 uw_reg;
f51dfe4d 53typedef uw_reg * uw_loc;
54
55typedef 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
67static int
68ia64_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, "
46da3ce5 180 "chfmech @ 0x%p, chfsig64 @ 0x%p, intstk @ 0x%p\n",
181 chfmech, chfsig64, intstk);
f51dfe4d 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 {
46da3ce5 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;
f51dfe4d 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 {
46da3ce5 255 uw_reg dirty_size = q_bsp - q_bspstore;
256 uw_reg q_rnat = (uw_reg) intstk->intstk$q_rnat;
f51dfe4d 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