Home | History | Annotate | Line # | Download | only in s390
tpf-unwind.h revision 1.1.1.10
      1 /* DWARF2 EH unwinding support for TPF OS.
      2    Copyright (C) 2004-2022 Free Software Foundation, Inc.
      3    Contributed by P.J. Darcy (darcypj (at) 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