OSDN Git Service

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