1169689Skan/* DWARF2 EH unwinding support for TPF OS.
2169689Skan   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
3169689Skan   Contributed by P.J. Darcy (darcypj@us.ibm.com).
4169689Skan
5169689SkanThis file is part of GCC.
6169689Skan
7169689SkanGCC is free software; you can redistribute it and/or modify it under
8169689Skanthe terms of the GNU General Public License as published by the Free
9169689SkanSoftware Foundation; either version 2, or (at your option) any later
10169689Skanversion.
11169689Skan
12169689SkanIn addition to the permissions in the GNU General Public License, the
13169689SkanFree Software Foundation gives you unlimited permission to link the
14169689Skancompiled version of this file into combinations with other programs,
15169689Skanand to distribute those combinations without any restriction coming
16169689Skanfrom the use of this file.  (The General Public License restrictions
17169689Skando apply in other respects; for example, they cover modification of
18169689Skanthe file, and distribution when not linked into a combined
19169689Skanexecutable.)
20169689Skan
21169689SkanGCC is distributed in the hope that it will be useful, but WITHOUT ANY
22169689SkanWARRANTY; without even the implied warranty of MERCHANTABILITY or
23169689SkanFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24169689Skanfor more details.
25169689Skan
26169689SkanYou should have received a copy of the GNU General Public License
27169689Skanalong with GCC; see the file COPYING.  If not, write to the Free
28169689SkanSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
29169689Skan02110-1301, USA.  */
30169689Skan
31169689Skan#include <dlfcn.h>
32169689Skan
33169689Skan/* Function Name: __isPATrange
34169689Skan   Parameters passed into it:  address to check
35169689Skan   Return Value: A 1 if address is in pat code "range", 0 if not
36169689Skan   Description: This function simply checks to see if the address
37169689Skan   passed to it is in the CP pat code range.  */
38169689Skan
39169689Skan#define MIN_PATRANGE 0x10000
40169689Skan#define MAX_PATRANGE 0x800000
41169689Skan
42169689Skanstatic inline unsigned int
43169689Skan__isPATrange (void *addr)
44169689Skan{
45169689Skan  if (addr > (void *)MIN_PATRANGE && addr < (void *)MAX_PATRANGE)
46169689Skan    return 1;
47169689Skan  else
48169689Skan    return 0;
49169689Skan}
50169689Skan
51169689Skan/* TPF return address offset from start of stack frame.  */
52169689Skan#define TPFRA_OFFSET 168
53169689Skan
54169689Skan/* Exceptions macro defined for TPF so that functions without
55169689Skan   dwarf frame information can be used with exceptions.  */
56169689Skan#define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state
57169689Skan
58169689Skanstatic _Unwind_Reason_Code
59169689Skans390_fallback_frame_state (struct _Unwind_Context *context,
60169689Skan			   _Unwind_FrameState *fs)
61169689Skan{
62169689Skan  unsigned long int regs;
63169689Skan  unsigned long int new_cfa;
64169689Skan  int i;
65169689Skan
66169689Skan  regs = *((unsigned long int *)
67169689Skan        (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET));
68169689Skan
69169689Skan  /* Are we going through special linkage code?  */
70169689Skan  if (__isPATrange (context->ra))
71169689Skan    {
72169689Skan
73169689Skan      /* Our return register isn't zero for end of stack, so
74169689Skan         check backward stackpointer to see if it is zero.  */
75169689Skan      if (regs == NULL)
76169689Skan         return _URC_END_OF_STACK;
77169689Skan
78169689Skan      /* No stack frame.  */
79169689Skan      fs->cfa_how = CFA_REG_OFFSET;
80169689Skan      fs->cfa_reg = 15;
81169689Skan      fs->cfa_offset = STACK_POINTER_OFFSET;
82169689Skan
83169689Skan      /* All registers remain unchanged ...  */
84169689Skan      for (i = 0; i < 32; i++)
85169689Skan	{
86169689Skan	  fs->regs.reg[i].how = REG_SAVED_REG;
87169689Skan	  fs->regs.reg[i].loc.reg = i;
88169689Skan	}
89169689Skan
90169689Skan      /* ... except for %r14, which is stored at CFA-112
91169689Skan	 and used as return address.  */
92169689Skan      fs->regs.reg[14].how = REG_SAVED_OFFSET;
93169689Skan      fs->regs.reg[14].loc.offset = TPFRA_OFFSET - STACK_POINTER_OFFSET;
94169689Skan      fs->retaddr_column = 14;
95169689Skan
96169689Skan      return _URC_NO_REASON;
97169689Skan    }
98169689Skan
99169689Skan  regs = *((unsigned long int *)
100169689Skan        (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET));
101169689Skan  new_cfa = regs + STACK_POINTER_OFFSET;
102169689Skan
103169689Skan  fs->cfa_how = CFA_REG_OFFSET;
104169689Skan  fs->cfa_reg = 15;
105169689Skan  fs->cfa_offset = new_cfa -
106169689Skan        (unsigned long int) context->cfa + STACK_POINTER_OFFSET;
107169689Skan
108169689Skan  for (i = 0; i < 16; i++)
109169689Skan    {
110169689Skan      fs->regs.reg[i].how = REG_SAVED_OFFSET;
111169689Skan      fs->regs.reg[i].loc.offset = regs + i*8 - new_cfa;
112169689Skan    }
113169689Skan
114169689Skan  for (i = 0; i < 4; i++)
115169689Skan    {
116169689Skan      fs->regs.reg[16 + i].how = REG_SAVED_OFFSET;
117169689Skan      fs->regs.reg[16 + i].loc.offset = regs + 16*8 + i*8 - new_cfa;
118169689Skan    }
119169689Skan
120169689Skan  fs->retaddr_column = 14;
121169689Skan
122169689Skan  return _URC_NO_REASON;
123169689Skan}
124169689Skan
125169689Skan/* Function Name: __tpf_eh_return
126169689Skan   Parameters passed into it: Destination address to jump to.
127169689Skan   Return Value: Converted Destination address if a Pat Stub exists.
128169689Skan   Description: This function swaps the unwinding return address
129169689Skan      with the cp stub code.  The original target return address is
130169689Skan      then stored into the tpf return address field.  The cp stub
131169689Skan      code is searched for by climbing back up the stack and
132169689Skan      comparing the tpf stored return address object address to
133169689Skan      that of the targets object address.  */
134169689Skan
135169689Skan#define CURRENT_STACK_PTR() \
136169689Skan  ({ register unsigned long int *stack_ptr asm ("%r15"); stack_ptr; })
137169689Skan
138169689Skan#define PREVIOUS_STACK_PTR() \
139169689Skan  ((unsigned long int *)(*(CURRENT_STACK_PTR())))
140169689Skan
141169689Skan#define RA_OFFSET 112
142169689Skan#define R15_OFFSET 120
143169689Skan#define TPFAREA_OFFSET 160
144169689Skan#define TPFAREA_SIZE STACK_POINTER_OFFSET-TPFAREA_OFFSET
145169689Skan#define INVALID_RETURN 0
146169689Skan
147169689Skanvoid * __tpf_eh_return (void *target);
148169689Skan
149169689Skanvoid *
150169689Skan__tpf_eh_return (void *target)
151169689Skan{
152169689Skan  Dl_info targetcodeInfo, currentcodeInfo;
153169689Skan  int retval;
154169689Skan  void *current, *stackptr, *destination_frame;
155169689Skan  unsigned long int shifter, is_a_stub;
156169689Skan
157169689Skan  is_a_stub = 0;
158169689Skan
159169689Skan  /* Get code info for target return's address.  */
160169689Skan  retval = dladdr (target, &targetcodeInfo);
161169689Skan
162169689Skan  /* Ensure the code info is valid (for target).  */
163169689Skan  if (retval != INVALID_RETURN)
164169689Skan    {
165169689Skan
166169689Skan      /* Get the stack pointer of the stack frame to be modified by
167169689Skan         the exception unwinder.  So that we can begin our climb
168169689Skan         there.  */
169169689Skan      stackptr = (void *) *((unsigned long int *) (*(PREVIOUS_STACK_PTR())));
170169689Skan
171169689Skan      /* Begin looping through stack frames.  Stop if invalid
172169689Skan         code information is retrieved or if a match between the
173169689Skan         current stack frame iteration shared object's address
174169689Skan         matches that of the target, calculated above.  */
175169689Skan      do
176169689Skan        {
177169689Skan          /* Get return address based on our stackptr iterator.  */
178169689Skan          current = (void *) *((unsigned long int *)
179169689Skan                      (stackptr+RA_OFFSET));
180169689Skan
181169689Skan          /* Is it a Pat Stub?  */
182169689Skan          if (__isPATrange (current))
183169689Skan            {
184169689Skan              /* Yes it was, get real return address
185169689Skan                 in TPF stack area.  */
186169689Skan              current = (void *) *((unsigned long int *)
187169689Skan                          (stackptr+TPFRA_OFFSET));
188169689Skan              is_a_stub = 1;
189169689Skan            }
190169689Skan
191169689Skan          /* Get codeinfo on RA so that we can figure out
192169689Skan             the module address.  */
193169689Skan          retval = dladdr (current, &currentcodeInfo);
194169689Skan
195169689Skan          /* Check that codeinfo for current stack frame is valid.
196169689Skan             Then compare the module address of current stack frame
197169689Skan             to target stack frame to determine if we have the pat
198169689Skan             stub address we want.  Also ensure we are dealing
199169689Skan             with a module crossing, stub return address. */
200169689Skan          if (is_a_stub && retval != INVALID_RETURN
201169689Skan             && targetcodeInfo.dli_fbase == currentcodeInfo.dli_fbase)
202169689Skan             {
203169689Skan               /* Yes! They are in the same module.
204169689Skan                  Force copy of TPF private stack area to
205169689Skan                  destination stack frame TPF private area. */
206169689Skan               destination_frame = (void *) *((unsigned long int *)
207169689Skan                   (*PREVIOUS_STACK_PTR() + R15_OFFSET));
208169689Skan
209169689Skan               /* Copy TPF linkage area from current frame to
210169689Skan                  destination frame.  */
211169689Skan               memcpy((void *) (destination_frame + TPFAREA_OFFSET),
212169689Skan                 (void *) (stackptr + TPFAREA_OFFSET), TPFAREA_SIZE);
213169689Skan
214169689Skan               /* Now overlay the
215169689Skan                  real target address into the TPF stack area of
216169689Skan                  the target frame we are jumping to.  */
217169689Skan               *((unsigned long int *) (destination_frame +
218169689Skan                   TPFRA_OFFSET)) = (unsigned long int) target;
219169689Skan
220169689Skan               /* Before returning the desired pat stub address to
221169689Skan                  the exception handling unwinder so that it can
222169689Skan                  actually do the "leap" shift out the low order
223169689Skan                  bit designated to determine if we are in 64BIT mode.
224169689Skan                  This is necessary for CTOA stubs.
225169689Skan                  Otherwise we leap one byte past where we want to
226169689Skan                  go to in the TPF pat stub linkage code.  */
227169689Skan               shifter = *((unsigned long int *)
228169689Skan                     (stackptr + RA_OFFSET));
229169689Skan
230169689Skan               shifter &= ~1ul;
231169689Skan
232169689Skan               /* Store Pat Stub Address in destination Stack Frame.  */
233169689Skan               *((unsigned long int *) (destination_frame +
234169689Skan                   RA_OFFSET)) = shifter;
235169689Skan
236169689Skan               /* Re-adjust pat stub address to go to correct place
237169689Skan                  in linkage.  */
238169689Skan               shifter = shifter - 4;
239169689Skan
240169689Skan               return (void *) shifter;
241169689Skan             }
242169689Skan
243169689Skan          /* Desired module pat stub not found ...
244169689Skan             Bump stack frame iterator.  */
245169689Skan          stackptr = (void *) *(unsigned long int *) stackptr;
246169689Skan
247169689Skan          is_a_stub = 0;
248169689Skan
249169689Skan        }  while (stackptr && retval != INVALID_RETURN
250169689Skan                && targetcodeInfo.dli_fbase != currentcodeInfo.dli_fbase);
251169689Skan    }
252169689Skan
253169689Skan  /* No pat stub found, could be a problem?  Simply return unmodified
254169689Skan     target address.  */
255169689Skan  return target;
256169689Skan}
257169689Skan
258