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