1/* DWARF2 EH unwinding support for TPF OS.
2   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3   Contributed by P.J. Darcy (darcypj@us.ibm.com).
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 2, or (at your option) any later
10version.
11
12In addition to the permissions in the GNU General Public License, the
13Free Software Foundation gives you unlimited permission to link the
14compiled version of this file into combinations with other programs,
15and to distribute those combinations without any restriction coming
16from the use of this file.  (The General Public License restrictions
17do apply in other respects; for example, they cover modification of
18the file, and distribution when not linked into a combined
19executable.)
20
21GCC is distributed in the hope that it will be useful, but WITHOUT ANY
22WARRANTY; without even the implied warranty of MERCHANTABILITY or
23FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24for more details.
25
26You should have received a copy of the GNU General Public License
27along with GCC; see the file COPYING.  If not, write to the Free
28Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
2902110-1301, USA.  */
30
31#include <dlfcn.h>
32
33/* Function Name: __isPATrange
34   Parameters passed into it:  address to check
35   Return Value: A 1 if address is in pat code "range", 0 if not
36   Description: This function simply checks to see if the address
37   passed to it is in the CP pat code range.  */
38
39#define MIN_PATRANGE 0x10000
40#define MAX_PATRANGE 0x800000
41
42static inline unsigned int
43__isPATrange (void *addr)
44{
45  if (addr > (void *)MIN_PATRANGE && addr < (void *)MAX_PATRANGE)
46    return 1;
47  else
48    return 0;
49}
50
51/* TPF return address offset from start of stack frame.  */
52#define TPFRA_OFFSET 168
53
54/* Exceptions macro defined for TPF so that functions without
55   dwarf frame information can be used with exceptions.  */
56#define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state
57
58static _Unwind_Reason_Code
59s390_fallback_frame_state (struct _Unwind_Context *context,
60			   _Unwind_FrameState *fs)
61{
62  unsigned long int regs;
63  unsigned long int new_cfa;
64  int i;
65
66  regs = *((unsigned long int *)
67        (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET));
68
69  /* Are we going through special linkage code?  */
70  if (__isPATrange (context->ra))
71    {
72
73      /* Our return register isn't zero for end of stack, so
74         check backward stackpointer to see if it is zero.  */
75      if (regs == NULL)
76         return _URC_END_OF_STACK;
77
78      /* No stack frame.  */
79      fs->cfa_how = CFA_REG_OFFSET;
80      fs->cfa_reg = 15;
81      fs->cfa_offset = STACK_POINTER_OFFSET;
82
83      /* All registers remain unchanged ...  */
84      for (i = 0; i < 32; i++)
85	{
86	  fs->regs.reg[i].how = REG_SAVED_REG;
87	  fs->regs.reg[i].loc.reg = i;
88	}
89
90      /* ... except for %r14, which is stored at CFA-112
91	 and used as return address.  */
92      fs->regs.reg[14].how = REG_SAVED_OFFSET;
93      fs->regs.reg[14].loc.offset = TPFRA_OFFSET - STACK_POINTER_OFFSET;
94      fs->retaddr_column = 14;
95
96      return _URC_NO_REASON;
97    }
98
99  regs = *((unsigned long int *)
100        (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET));
101  new_cfa = regs + STACK_POINTER_OFFSET;
102
103  fs->cfa_how = CFA_REG_OFFSET;
104  fs->cfa_reg = 15;
105  fs->cfa_offset = new_cfa -
106        (unsigned long int) context->cfa + STACK_POINTER_OFFSET;
107
108  for (i = 0; i < 16; i++)
109    {
110      fs->regs.reg[i].how = REG_SAVED_OFFSET;
111      fs->regs.reg[i].loc.offset = regs + i*8 - new_cfa;
112    }
113
114  for (i = 0; i < 4; i++)
115    {
116      fs->regs.reg[16 + i].how = REG_SAVED_OFFSET;
117      fs->regs.reg[16 + i].loc.offset = regs + 16*8 + i*8 - new_cfa;
118    }
119
120  fs->retaddr_column = 14;
121
122  return _URC_NO_REASON;
123}
124
125/* Function Name: __tpf_eh_return
126   Parameters passed into it: Destination address to jump to.
127   Return Value: Converted Destination address if a Pat Stub exists.
128   Description: This function swaps the unwinding return address
129      with the cp stub code.  The original target return address is
130      then stored into the tpf return address field.  The cp stub
131      code is searched for by climbing back up the stack and
132      comparing the tpf stored return address object address to
133      that of the targets object address.  */
134
135#define CURRENT_STACK_PTR() \
136  ({ register unsigned long int *stack_ptr asm ("%r15"); stack_ptr; })
137
138#define PREVIOUS_STACK_PTR() \
139  ((unsigned long int *)(*(CURRENT_STACK_PTR())))
140
141#define RA_OFFSET 112
142#define R15_OFFSET 120
143#define TPFAREA_OFFSET 160
144#define TPFAREA_SIZE STACK_POINTER_OFFSET-TPFAREA_OFFSET
145#define INVALID_RETURN 0
146
147void * __tpf_eh_return (void *target);
148
149void *
150__tpf_eh_return (void *target)
151{
152  Dl_info targetcodeInfo, currentcodeInfo;
153  int retval;
154  void *current, *stackptr, *destination_frame;
155  unsigned long int shifter, is_a_stub;
156
157  is_a_stub = 0;
158
159  /* Get code info for target return's address.  */
160  retval = dladdr (target, &targetcodeInfo);
161
162  /* Ensure the code info is valid (for target).  */
163  if (retval != INVALID_RETURN)
164    {
165
166      /* Get the stack pointer of the stack frame to be modified by
167         the exception unwinder.  So that we can begin our climb
168         there.  */
169      stackptr = (void *) *((unsigned long int *) (*(PREVIOUS_STACK_PTR())));
170
171      /* Begin looping through stack frames.  Stop if invalid
172         code information is retrieved or if a match between the
173         current stack frame iteration shared object's address
174         matches that of the target, calculated above.  */
175      do
176        {
177          /* Get return address based on our stackptr iterator.  */
178          current = (void *) *((unsigned long int *)
179                      (stackptr+RA_OFFSET));
180
181          /* Is it a Pat Stub?  */
182          if (__isPATrange (current))
183            {
184              /* Yes it was, get real return address
185                 in TPF stack area.  */
186              current = (void *) *((unsigned long int *)
187                          (stackptr+TPFRA_OFFSET));
188              is_a_stub = 1;
189            }
190
191          /* Get codeinfo on RA so that we can figure out
192             the module address.  */
193          retval = dladdr (current, &currentcodeInfo);
194
195          /* Check that codeinfo for current stack frame is valid.
196             Then compare the module address of current stack frame
197             to target stack frame to determine if we have the pat
198             stub address we want.  Also ensure we are dealing
199             with a module crossing, stub return address. */
200          if (is_a_stub && retval != INVALID_RETURN
201             && targetcodeInfo.dli_fbase == currentcodeInfo.dli_fbase)
202             {
203               /* Yes! They are in the same module.
204                  Force copy of TPF private stack area to
205                  destination stack frame TPF private area. */
206               destination_frame = (void *) *((unsigned long int *)
207                   (*PREVIOUS_STACK_PTR() + R15_OFFSET));
208
209               /* Copy TPF linkage area from current frame to
210                  destination frame.  */
211               memcpy((void *) (destination_frame + TPFAREA_OFFSET),
212                 (void *) (stackptr + TPFAREA_OFFSET), TPFAREA_SIZE);
213
214               /* Now overlay the
215                  real target address into the TPF stack area of
216                  the target frame we are jumping to.  */
217               *((unsigned long int *) (destination_frame +
218                   TPFRA_OFFSET)) = (unsigned long int) target;
219
220               /* Before returning the desired pat stub address to
221                  the exception handling unwinder so that it can
222                  actually do the "leap" shift out the low order
223                  bit designated to determine if we are in 64BIT mode.
224                  This is necessary for CTOA stubs.
225                  Otherwise we leap one byte past where we want to
226                  go to in the TPF pat stub linkage code.  */
227               shifter = *((unsigned long int *)
228                     (stackptr + RA_OFFSET));
229
230               shifter &= ~1ul;
231
232               /* Store Pat Stub Address in destination Stack Frame.  */
233               *((unsigned long int *) (destination_frame +
234                   RA_OFFSET)) = shifter;
235
236               /* Re-adjust pat stub address to go to correct place
237                  in linkage.  */
238               shifter = shifter - 4;
239
240               return (void *) shifter;
241             }
242
243          /* Desired module pat stub not found ...
244             Bump stack frame iterator.  */
245          stackptr = (void *) *(unsigned long int *) stackptr;
246
247          is_a_stub = 0;
248
249        }  while (stackptr && retval != INVALID_RETURN
250                && targetcodeInfo.dli_fbase != currentcodeInfo.dli_fbase);
251    }
252
253  /* No pat stub found, could be a problem?  Simply return unmodified
254     target address.  */
255  return target;
256}
257
258