tpf-unwind.h revision 1.1.1.11 1 1.1 mrg /* DWARF2 EH unwinding support for TPF OS.
2 1.1.1.11 mrg Copyright (C) 2004-2024 Free Software Foundation, Inc.
3 1.1 mrg Contributed by P.J. Darcy (darcypj (at) us.ibm.com).
4 1.1 mrg
5 1.1 mrg This file is part of GCC.
6 1.1 mrg
7 1.1 mrg GCC is free software; you can redistribute it and/or modify it under
8 1.1 mrg the terms of the GNU General Public License as published by the Free
9 1.1 mrg Software Foundation; either version 3, or (at your option) any later
10 1.1 mrg version.
11 1.1 mrg
12 1.1 mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 1.1 mrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 1.1 mrg for more details.
16 1.1 mrg
17 1.1 mrg Under Section 7 of GPL version 3, you are granted additional
18 1.1 mrg permissions described in the GCC Runtime Library Exception, version
19 1.1 mrg 3.1, as published by the Free Software Foundation.
20 1.1 mrg
21 1.1 mrg You should have received a copy of the GNU General Public License and
22 1.1 mrg a copy of the GCC Runtime Library Exception along with this program;
23 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 1.1 mrg <http://www.gnu.org/licenses/>. */
25 1.1 mrg
26 1.1 mrg #include <dlfcn.h>
27 1.1.1.2 mrg #include <stdbool.h>
28 1.1 mrg
29 1.1 mrg /* Function Name: __isPATrange
30 1.1 mrg Parameters passed into it: address to check
31 1.1 mrg Return Value: A 1 if address is in pat code "range", 0 if not
32 1.1 mrg Description: This function simply checks to see if the address
33 1.1 mrg passed to it is in the CP pat code range. */
34 1.1 mrg
35 1.1.1.9 mrg #define CP_CNF 0x000000000000c18u /* location of BSS CINFC pointer */
36 1.1.1.9 mrg #define cinfc_fast(TAG) (void *) \
37 1.1.1.9 mrg *((unsigned long *) *(unsigned long *) (CP_CNF) + (TAG))
38 1.1.1.9 mrg #define CINFC_CMRESET 187
39 1.1.1.9 mrg #define CINTFC_CMCENBKST 431
40 1.1.1.9 mrg #define CINTFC_CMCENBKED 432
41 1.1 mrg
42 1.1 mrg static inline unsigned int
43 1.1 mrg __isPATrange (void *addr)
44 1.1 mrg {
45 1.1.1.9 mrg return !!(addr > cinfc_fast (CINTFC_CMCENBKST)
46 1.1.1.9 mrg && addr < cinfc_fast (CINTFC_CMCENBKED));
47 1.1.1.9 mrg }
48 1.1.1.9 mrg
49 1.1.1.9 mrg static inline unsigned int
50 1.1.1.9 mrg __isSkipResetAddr (void *addr)
51 1.1.1.9 mrg {
52 1.1.1.9 mrg return !!(addr == cinfc_fast (CINFC_CMRESET));
53 1.1 mrg }
54 1.1 mrg
55 1.1 mrg /* TPF return address offset from start of stack frame. */
56 1.1.1.9 mrg #define ICST_CRET 168
57 1.1.1.9 mrg #define ICST_SRET 320
58 1.1 mrg
59 1.1 mrg /* Exceptions macro defined for TPF so that functions without
60 1.1 mrg dwarf frame information can be used with exceptions. */
61 1.1 mrg #define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state
62 1.1 mrg
63 1.1 mrg static _Unwind_Reason_Code
64 1.1 mrg s390_fallback_frame_state (struct _Unwind_Context *context,
65 1.1 mrg _Unwind_FrameState *fs)
66 1.1 mrg {
67 1.1 mrg unsigned long int regs;
68 1.1 mrg unsigned long int new_cfa;
69 1.1 mrg int i;
70 1.1 mrg
71 1.1 mrg regs = *((unsigned long int *)
72 1.1 mrg (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET));
73 1.1 mrg
74 1.1 mrg /* Are we going through special linkage code? */
75 1.1.1.9 mrg if (__isPATrange (context->ra) || __isSkipResetAddr (context->ra))
76 1.1 mrg {
77 1.1 mrg
78 1.1 mrg /* Our return register isn't zero for end of stack, so
79 1.1 mrg check backward stackpointer to see if it is zero. */
80 1.1.1.9 mrg if (regs == 0)
81 1.1 mrg return _URC_END_OF_STACK;
82 1.1 mrg
83 1.1 mrg /* No stack frame. */
84 1.1 mrg fs->regs.cfa_how = CFA_REG_OFFSET;
85 1.1 mrg fs->regs.cfa_reg = 15;
86 1.1 mrg fs->regs.cfa_offset = STACK_POINTER_OFFSET;
87 1.1 mrg
88 1.1 mrg /* All registers remain unchanged ... */
89 1.1 mrg for (i = 0; i < 32; i++)
90 1.1 mrg {
91 1.1.1.11 mrg fs->regs.how[i] = REG_SAVED_REG;
92 1.1 mrg fs->regs.reg[i].loc.reg = i;
93 1.1 mrg }
94 1.1 mrg
95 1.1.1.9 mrg /* ... except for %r14, which is stored at CFA+offset where offset
96 1.1.1.9 mrg is displacment of ICST_CRET or ICST_SRET from CFA */
97 1.1.1.9 mrg if ( __isPATrange(context->ra) ) {
98 1.1.1.11 mrg fs->regs.how[14] = REG_SAVED_OFFSET;
99 1.1.1.9 mrg fs->regs.reg[14].loc.offset = ICST_CRET - STACK_POINTER_OFFSET;
100 1.1.1.9 mrg fs->retaddr_column = 14;
101 1.1.1.9 mrg } else {
102 1.1.1.11 mrg fs->regs.how[14] = REG_SAVED_OFFSET;
103 1.1.1.9 mrg fs->regs.reg[14].loc.offset = ICST_SRET - STACK_POINTER_OFFSET;
104 1.1.1.9 mrg fs->retaddr_column = 14;
105 1.1.1.9 mrg
106 1.1.1.9 mrg }
107 1.1 mrg
108 1.1 mrg return _URC_NO_REASON;
109 1.1 mrg }
110 1.1 mrg
111 1.1 mrg regs = *((unsigned long int *)
112 1.1 mrg (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET));
113 1.1 mrg new_cfa = regs + STACK_POINTER_OFFSET;
114 1.1 mrg
115 1.1 mrg fs->regs.cfa_how = CFA_REG_OFFSET;
116 1.1 mrg fs->regs.cfa_reg = 15;
117 1.1 mrg fs->regs.cfa_offset = new_cfa -
118 1.1 mrg (unsigned long int) context->cfa + STACK_POINTER_OFFSET;
119 1.1 mrg
120 1.1 mrg for (i = 0; i < 16; i++)
121 1.1 mrg {
122 1.1.1.11 mrg fs->regs.how[i] = REG_SAVED_OFFSET;
123 1.1 mrg fs->regs.reg[i].loc.offset = regs + i*8 - new_cfa;
124 1.1 mrg }
125 1.1 mrg
126 1.1 mrg for (i = 0; i < 4; i++)
127 1.1 mrg {
128 1.1.1.11 mrg fs->regs.how[16 + i] = REG_SAVED_OFFSET;
129 1.1 mrg fs->regs.reg[16 + i].loc.offset = regs + 16*8 + i*8 - new_cfa;
130 1.1 mrg }
131 1.1 mrg
132 1.1 mrg fs->retaddr_column = 14;
133 1.1 mrg
134 1.1 mrg return _URC_NO_REASON;
135 1.1 mrg }
136 1.1 mrg
137 1.1 mrg /* Function Name: __tpf_eh_return
138 1.1 mrg Parameters passed into it: Destination address to jump to.
139 1.1 mrg Return Value: Converted Destination address if a Pat Stub exists.
140 1.1 mrg Description: This function swaps the unwinding return address
141 1.1 mrg with the cp stub code. The original target return address is
142 1.1 mrg then stored into the tpf return address field. The cp stub
143 1.1 mrg code is searched for by climbing back up the stack and
144 1.1 mrg comparing the tpf stored return address object address to
145 1.1 mrg that of the targets object address. */
146 1.1 mrg
147 1.1 mrg #define CURRENT_STACK_PTR() \
148 1.1 mrg ({ register unsigned long int *stack_ptr asm ("%r15"); stack_ptr; })
149 1.1 mrg
150 1.1 mrg #define PREVIOUS_STACK_PTR() \
151 1.1 mrg ((unsigned long int *)(*(CURRENT_STACK_PTR())))
152 1.1 mrg
153 1.1 mrg #define RA_OFFSET 112
154 1.1 mrg #define R15_OFFSET 120
155 1.1 mrg #define TPFAREA_OFFSET 160
156 1.1 mrg #define TPFAREA_SIZE STACK_POINTER_OFFSET-TPFAREA_OFFSET
157 1.1 mrg #define INVALID_RETURN 0
158 1.1 mrg
159 1.1.1.9 mrg #define LOWCORE_PAGE3_ADDR 4032
160 1.1.1.9 mrg #define PG3_SKIPPING_OFFSET 18
161 1.1.1.9 mrg
162 1.1.1.2 mrg void * __tpf_eh_return (void *target, void *origRA);
163 1.1 mrg
164 1.1 mrg void *
165 1.1.1.2 mrg __tpf_eh_return (void *target, void *origRA)
166 1.1 mrg {
167 1.1 mrg Dl_info targetcodeInfo, currentcodeInfo;
168 1.1 mrg int retval;
169 1.1 mrg void *current, *stackptr, *destination_frame;
170 1.1.1.9 mrg unsigned char *skipFlagAddress;
171 1.1.1.2 mrg unsigned long int shifter;
172 1.1.1.9 mrg bool is_a_stub;
173 1.1 mrg
174 1.1.1.2 mrg is_a_stub = false;
175 1.1 mrg
176 1.1 mrg /* Get code info for target return's address. */
177 1.1 mrg retval = dladdr (target, &targetcodeInfo);
178 1.1 mrg
179 1.1 mrg /* Ensure the code info is valid (for target). */
180 1.1 mrg if (retval != INVALID_RETURN)
181 1.1 mrg {
182 1.1.1.9 mrg /* Begin climbing stack searching for target address. */
183 1.1.1.9 mrg stackptr = (void *) *(CURRENT_STACK_PTR());
184 1.1.1.9 mrg
185 1.1.1.9 mrg /* Get return address based on our stackptr. */
186 1.1.1.9 mrg current = (void *) *(unsigned long *) (stackptr + RA_OFFSET);
187 1.1.1.9 mrg
188 1.1.1.9 mrg /* Is current return address our initiating exception stack
189 1.1.1.9 mrg frame? If not, climb the stack one more frame. */
190 1.1.1.9 mrg if (current != origRA) {
191 1.1.1.9 mrg stackptr = (void *) *(unsigned long *) stackptr;
192 1.1.1.9 mrg }
193 1.1 mrg
194 1.1 mrg /* Begin looping through stack frames. Stop if invalid
195 1.1 mrg code information is retrieved or if a match between the
196 1.1 mrg current stack frame iteration shared object's address
197 1.1 mrg matches that of the target, calculated above. */
198 1.1 mrg do
199 1.1 mrg {
200 1.1.1.9 mrg /* Get return address based on our stackptr iterator. */
201 1.1.1.9 mrg current = (void *) *(unsigned long *) (stackptr + RA_OFFSET);
202 1.1.1.9 mrg
203 1.1.1.9 mrg /* Is it a Pat Stub? */
204 1.1.1.9 mrg if (__isPATrange (current)
205 1.1.1.9 mrg || (__isSkipResetAddr (current)
206 1.1.1.9 mrg && __isPATrange ((void *) *(unsigned long *) (stackptr
207 1.1.1.9 mrg + ICST_SRET))))
208 1.1.1.9 mrg {
209 1.1.1.9 mrg /* Yes it was, get real return address in TPF stack area. */
210 1.1.1.9 mrg current = (void *) *(unsigned long *) (stackptr + ICST_CRET);
211 1.1.1.9 mrg is_a_stub = true;
212 1.1.1.9 mrg }
213 1.1 mrg
214 1.1 mrg /* Get codeinfo on RA so that we can figure out
215 1.1 mrg the module address. */
216 1.1 mrg retval = dladdr (current, ¤tcodeInfo);
217 1.1 mrg
218 1.1 mrg /* Check that codeinfo for current stack frame is valid.
219 1.1 mrg Then compare the module address of current stack frame
220 1.1 mrg to target stack frame to determine if we have the pat
221 1.1 mrg stub address we want. Also ensure we are dealing
222 1.1 mrg with a module crossing, stub return address. */
223 1.1 mrg if (is_a_stub && retval != INVALID_RETURN
224 1.1 mrg && targetcodeInfo.dli_fbase == currentcodeInfo.dli_fbase)
225 1.1 mrg {
226 1.1 mrg /* Yes! They are in the same module.
227 1.1 mrg Force copy of TPF private stack area to
228 1.1 mrg destination stack frame TPF private area. */
229 1.1 mrg destination_frame = (void *) *((unsigned long int *)
230 1.1 mrg (*PREVIOUS_STACK_PTR() + R15_OFFSET));
231 1.1 mrg
232 1.1 mrg /* Copy TPF linkage area from current frame to
233 1.1 mrg destination frame. */
234 1.1 mrg memcpy((void *) (destination_frame + TPFAREA_OFFSET),
235 1.1 mrg (void *) (stackptr + TPFAREA_OFFSET), TPFAREA_SIZE);
236 1.1 mrg
237 1.1 mrg /* Now overlay the
238 1.1 mrg real target address into the TPF stack area of
239 1.1 mrg the target frame we are jumping to. */
240 1.1.1.9 mrg *(unsigned long *) (destination_frame + ICST_CRET) =
241 1.1.1.9 mrg (unsigned long) target;
242 1.1 mrg
243 1.1 mrg /* Before returning the desired pat stub address to
244 1.1 mrg the exception handling unwinder so that it can
245 1.1 mrg actually do the "leap" shift out the low order
246 1.1 mrg bit designated to determine if we are in 64BIT mode.
247 1.1 mrg This is necessary for CTOA stubs.
248 1.1 mrg Otherwise we leap one byte past where we want to
249 1.1 mrg go to in the TPF pat stub linkage code. */
250 1.1.1.9 mrg shifter = *(unsigned long *) (stackptr + RA_OFFSET);
251 1.1 mrg
252 1.1 mrg shifter &= ~1ul;
253 1.1 mrg
254 1.1 mrg /* Store Pat Stub Address in destination Stack Frame. */
255 1.1 mrg *((unsigned long int *) (destination_frame +
256 1.1 mrg RA_OFFSET)) = shifter;
257 1.1 mrg
258 1.1 mrg /* Re-adjust pat stub address to go to correct place
259 1.1 mrg in linkage. */
260 1.1 mrg shifter = shifter - 4;
261 1.1 mrg
262 1.1.1.9 mrg /* Reset the Function Trace Skipping Switch to re-enable */
263 1.1.1.9 mrg /* recording Trace entries if it was turned off. */
264 1.1.1.9 mrg skipFlagAddress =
265 1.1.1.9 mrg (unsigned char *) *(unsigned long *) LOWCORE_PAGE3_ADDR;
266 1.1.1.9 mrg skipFlagAddress += PG3_SKIPPING_OFFSET;
267 1.1.1.9 mrg *skipFlagAddress = '\x00';
268 1.1.1.9 mrg
269 1.1 mrg return (void *) shifter;
270 1.1 mrg }
271 1.1 mrg
272 1.1 mrg /* Desired module pat stub not found ...
273 1.1 mrg Bump stack frame iterator. */
274 1.1 mrg stackptr = (void *) *(unsigned long int *) stackptr;
275 1.1 mrg
276 1.1.1.2 mrg is_a_stub = false;
277 1.1 mrg
278 1.1 mrg } while (stackptr && retval != INVALID_RETURN
279 1.1 mrg && targetcodeInfo.dli_fbase != currentcodeInfo.dli_fbase);
280 1.1 mrg }
281 1.1 mrg
282 1.1.1.9 mrg /* Reset the Function Trace Skipping Switch to re-enable */
283 1.1.1.9 mrg /* recording Trace entries if it was turned off. */
284 1.1.1.9 mrg skipFlagAddress = (unsigned char *) *(unsigned long *) LOWCORE_PAGE3_ADDR;
285 1.1.1.9 mrg skipFlagAddress += PG3_SKIPPING_OFFSET;
286 1.1.1.9 mrg *skipFlagAddress = '\x00';
287 1.1.1.9 mrg
288 1.1 mrg /* No pat stub found, could be a problem? Simply return unmodified
289 1.1 mrg target address. */
290 1.1 mrg return target;
291 1.1 mrg }
292