Home | History | Annotate | Line # | Download | only in pa
      1   1.1  mrg /* Subroutine for function pointer canonicalization on PA-RISC with ELF32.
      2  1.11  mrg    Copyright (C) 2002-2022 Free Software Foundation, Inc.
      3   1.1  mrg    Contributed by John David Anglin (dave.anglin (at) nrc.ca).
      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 
     27   1.1  mrg /* WARNING: The code is this function depends on internal and undocumented
     28   1.1  mrg    details of the GNU linker and dynamic loader as implemented for parisc
     29   1.1  mrg    linux.  */
     30   1.1  mrg 
     31   1.1  mrg /* This MUST match the defines sysdeps/hppa/dl-machine.h and
     32   1.1  mrg    bfd/elf32-hppa.c.  */
     33   1.1  mrg #define GOT_FROM_PLT_STUB (4*4)
     34   1.1  mrg 
     35   1.1  mrg /* List of byte offsets in _dl_runtime_resolve to search for "bl" branches.
     36   1.1  mrg    The first "bl" branch instruction found MUST be a call to fixup.  See
     37   1.1  mrg    the define for TRAMPOLINE_TEMPLATE in sysdeps/hppa/dl-machine.h.  If
     38   1.1  mrg    the trampoline template is changed, the list must be appropriately
     39   1.1  mrg    updated.  The offset of -4 allows for a magic branch at the start of
     40   1.1  mrg    the template should it be necessary to change the current branch
     41   1.1  mrg    position.  */
     42   1.1  mrg #define NOFFSETS 2
     43   1.3  mrg static int fixup_branch_offset[NOFFSETS] = { -4, 32 };
     44   1.1  mrg 
     45   1.1  mrg #define GET_FIELD(X, FROM, TO) \
     46   1.1  mrg   ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1))
     47   1.1  mrg #define SIGN_EXTEND(VAL,BITS) \
     48   1.4  mrg   ((int) ((VAL) >> ((BITS) - 1) ? ((unsigned)(-1) << (BITS)) | (VAL) : (VAL)))
     49   1.1  mrg 
     50   1.1  mrg struct link_map;
     51   1.1  mrg typedef int (*fptr_t) (void);
     52   1.1  mrg typedef int (*fixup_t) (struct link_map *, unsigned int);
     53   1.1  mrg extern unsigned int _GLOBAL_OFFSET_TABLE_;
     54   1.1  mrg 
     55   1.5  mrg static inline int
     56   1.8  mrg _dl_read_access_allowed (unsigned int addr)
     57   1.5  mrg {
     58   1.5  mrg   int result;
     59   1.5  mrg 
     60   1.5  mrg   asm ("proberi (%1),3,%0" : "=r" (result) : "r" (addr) : );
     61   1.5  mrg 
     62   1.5  mrg   return result;
     63   1.5  mrg }
     64   1.5  mrg 
     65  1.10  mrg #pragma GCC diagnostic push
     66  1.10  mrg #pragma GCC diagnostic ignored "-Warray-bounds"
     67  1.10  mrg 
     68   1.1  mrg /* __canonicalize_funcptr_for_compare must be hidden so that it is not
     69   1.1  mrg    placed in the dynamic symbol table.  Like millicode functions, it
     70   1.1  mrg    must be linked into all binaries in order access the got table of
     71   1.1  mrg    that binary.  However, we don't use the millicode calling convention
     72   1.1  mrg    and the routine must be a normal function so that it can be compiled
     73   1.1  mrg    as pic code.  */
     74   1.1  mrg unsigned int __canonicalize_funcptr_for_compare (fptr_t)
     75   1.1  mrg       __attribute__ ((visibility ("hidden")));
     76   1.1  mrg 
     77   1.1  mrg unsigned int
     78   1.1  mrg __canonicalize_funcptr_for_compare (fptr_t fptr)
     79   1.1  mrg {
     80   1.3  mrg   static unsigned int fixup_plabel[2] __attribute__((used));
     81   1.3  mrg   fixup_t fixup;
     82   1.8  mrg   volatile unsigned int *plabel;
     83   1.8  mrg   unsigned int *got, *iptr, reloc_offset;
     84   1.3  mrg   int i;
     85   1.1  mrg 
     86   1.1  mrg   /* -1 and page 0 are special.  -1 is used in crtend to mark the end of
     87   1.1  mrg      a list of function pointers.  Also return immediately if the plabel
     88   1.1  mrg      bit is not set in the function pointer.  In this case, the function
     89   1.1  mrg      pointer points directly to the function.  */
     90   1.1  mrg   if ((int) fptr == -1 || (unsigned int) fptr < 4096 || !((int) fptr & 2))
     91   1.1  mrg     return (unsigned int) fptr;
     92   1.1  mrg 
     93   1.1  mrg   /* The function pointer points to a function descriptor (plabel).  If
     94   1.1  mrg      the plabel hasn't been resolved, the first word of the plabel points
     95   1.1  mrg      to the entry of the PLT stub just before the global offset table.
     96   1.1  mrg      The second word in the plabel contains the relocation offset for the
     97   1.1  mrg      function.  */
     98   1.8  mrg   plabel = (volatile unsigned int *) ((unsigned int) fptr & ~3);
     99   1.8  mrg   if (!_dl_read_access_allowed ((unsigned int)plabel))
    100   1.5  mrg     return (unsigned int) fptr;
    101   1.5  mrg 
    102   1.5  mrg   /* Load first word of candidate descriptor.  It should be a pointer
    103   1.5  mrg      with word alignment and point to memory that can be read.  */
    104   1.5  mrg   got = (unsigned int *) plabel[0];
    105   1.5  mrg   if (((unsigned int) got & 3) != 0
    106   1.8  mrg       || !_dl_read_access_allowed ((unsigned int)got))
    107   1.5  mrg     return (unsigned int) fptr;
    108   1.5  mrg 
    109   1.8  mrg   /* We need to load the relocation offset before the function address.  */
    110   1.8  mrg   reloc_offset = plabel[1];
    111   1.8  mrg   __sync_synchronize();
    112   1.1  mrg   got = (unsigned int *) (plabel[0] + GOT_FROM_PLT_STUB);
    113   1.1  mrg 
    114   1.1  mrg   /* Return the address of the function if the plabel has been resolved.  */
    115   1.1  mrg   if (got !=  &_GLOBAL_OFFSET_TABLE_)
    116   1.1  mrg     return plabel[0];
    117   1.1  mrg 
    118   1.3  mrg   /* Find the first "bl" branch in the offset search list.  This is a
    119   1.3  mrg      call to _dl_fixup or a magic branch to fixup at the beginning of the
    120   1.3  mrg      trampoline template.  The fixup function does the actual runtime
    121   1.3  mrg      resolution of function descriptors.  We only look for "bl" branches
    122   1.3  mrg      with a 17-bit pc-relative displacement.  */
    123   1.3  mrg   for (i = 0; i < NOFFSETS; i++)
    124   1.1  mrg     {
    125   1.3  mrg       iptr = (unsigned int *) (got[-2] + fixup_branch_offset[i]);
    126   1.3  mrg       if ((*iptr & 0xfc00e000) == 0xe8000000)
    127   1.3  mrg 	break;
    128   1.3  mrg     }
    129   1.1  mrg 
    130   1.3  mrg   /* This should not happen... */
    131   1.3  mrg   if (i == NOFFSETS)
    132   1.3  mrg     return ~0;
    133   1.3  mrg 
    134   1.3  mrg   /* Extract the 17-bit displacement from the instruction.  */
    135   1.3  mrg   iptr += SIGN_EXTEND (GET_FIELD (*iptr, 19, 28) |
    136   1.3  mrg 		       GET_FIELD (*iptr, 29, 29) << 10 |
    137   1.3  mrg 		       GET_FIELD (*iptr, 11, 15) << 11 |
    138   1.3  mrg 		       GET_FIELD (*iptr, 31, 31) << 16, 17);
    139   1.3  mrg 
    140   1.3  mrg   /* Build a plabel for an indirect call to _dl_fixup.  */
    141   1.3  mrg   fixup_plabel[0] = (unsigned int) iptr + 8;	/* address of fixup */
    142   1.3  mrg   fixup_plabel[1] = got[-1];			/* ltp for fixup */
    143   1.7  mrg   fixup = (fixup_t) ((int) fixup_plabel | 2);
    144   1.1  mrg 
    145   1.1  mrg   /* Call fixup to resolve the function address.  got[1] contains the
    146   1.1  mrg      link_map pointer and plabel[1] the relocation offset.  */
    147   1.8  mrg   fixup ((struct link_map *) got[1], reloc_offset);
    148   1.1  mrg 
    149   1.1  mrg   return plabel[0];
    150   1.1  mrg }
    151  1.10  mrg 
    152  1.10  mrg #pragma GCC diagnostic pop
    153