Home | History | Annotate | Line # | Download | only in s390
      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, &currentcodeInfo);
    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