]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
f51dfe4d | 51 | typedef unsigned int uint; |
46da3ce5 | 52 | typedef unsigned __int64 uw_reg; |
f51dfe4d | 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 | |
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, " | |
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 |