11638Srgrimes/* Subroutine for function pointer canonicalization on PA-RISC with ELF32.
21638Srgrimes   Copyright (C) 2002-2020 Free Software Foundation, Inc.
31638Srgrimes   Contributed by John David Anglin (dave.anglin@nrc.ca).
41638Srgrimes
51638SrgrimesThis file is part of GCC.
61638Srgrimes
71638SrgrimesGCC is free software; you can redistribute it and/or modify it under
81638Srgrimesthe terms of the GNU General Public License as published by the Free
91638SrgrimesSoftware Foundation; either version 3, or (at your option) any later
101638Srgrimesversion.
111638Srgrimes
121638SrgrimesGCC is distributed in the hope that it will be useful, but WITHOUT ANY
131638SrgrimesWARRANTY; without even the implied warranty of MERCHANTABILITY or
141638SrgrimesFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
151638Srgrimesfor more details.
161638Srgrimes
171638SrgrimesUnder Section 7 of GPL version 3, you are granted additional
181638Srgrimespermissions described in the GCC Runtime Library Exception, version
191638Srgrimes3.1, as published by the Free Software Foundation.
201638Srgrimes
211638SrgrimesYou should have received a copy of the GNU General Public License and
221638Srgrimesa copy of the GCC Runtime Library Exception along with this program;
231638Srgrimessee the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
241638Srgrimes<http://www.gnu.org/licenses/>.  */
251638Srgrimes
261638Srgrimes
271638Srgrimes/* WARNING: The code is this function depends on internal and undocumented
281638Srgrimes   details of the GNU linker and dynamic loader as implemented for parisc
291638Srgrimes   linux.  */
301638Srgrimes
311638Srgrimes/* This MUST match the defines sysdeps/hppa/dl-machine.h and
321638Srgrimes   bfd/elf32-hppa.c.  */
3350476Speter#define GOT_FROM_PLT_STUB (4*4)
341638Srgrimes
3569283Sru/* List of byte offsets in _dl_runtime_resolve to search for "bl" branches.
361638Srgrimes   The first "bl" branch instruction found MUST be a call to fixup.  See
371638Srgrimes   the define for TRAMPOLINE_TEMPLATE in sysdeps/hppa/dl-machine.h.  If
381638Srgrimes   the trampoline template is changed, the list must be appropriately
391638Srgrimes   updated.  The offset of -4 allows for a magic branch at the start of
401638Srgrimes   the template should it be necessary to change the current branch
411638Srgrimes   position.  */
421638Srgrimes#define NOFFSETS 2
431638Srgrimesstatic int fixup_branch_offset[NOFFSETS] = { -4, 32 };
441638Srgrimes
451638Srgrimes#define GET_FIELD(X, FROM, TO) \
461638Srgrimes  ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1))
471638Srgrimes#define SIGN_EXTEND(VAL,BITS) \
481638Srgrimes  ((int) ((VAL) >> ((BITS) - 1) ? ((unsigned)(-1) << (BITS)) | (VAL) : (VAL)))
491638Srgrimes
501638Srgrimesstruct link_map;
511638Srgrimestypedef int (*fptr_t) (void);
521638Srgrimestypedef int (*fixup_t) (struct link_map *, unsigned int);
531638Srgrimesextern unsigned int _GLOBAL_OFFSET_TABLE_;
541638Srgrimes
551638Srgrimesstatic inline int
561638Srgrimes_dl_read_access_allowed (unsigned int addr)
571638Srgrimes{
581638Srgrimes  int result;
591638Srgrimes
601638Srgrimes  asm ("proberi (%1),3,%0" : "=r" (result) : "r" (addr) : );
611638Srgrimes
621638Srgrimes  return result;
631638Srgrimes}
641638Srgrimes
651638Srgrimes#pragma GCC diagnostic push
661638Srgrimes#pragma GCC diagnostic ignored "-Warray-bounds"
671638Srgrimes
681638Srgrimes/* __canonicalize_funcptr_for_compare must be hidden so that it is not
691638Srgrimes   placed in the dynamic symbol table.  Like millicode functions, it
701638Srgrimes   must be linked into all binaries in order access the got table of
711638Srgrimes   that binary.  However, we don't use the millicode calling convention
721638Srgrimes   and the routine must be a normal function so that it can be compiled
731638Srgrimes   as pic code.  */
741638Srgrimesunsigned int __canonicalize_funcptr_for_compare (fptr_t)
751638Srgrimes      __attribute__ ((visibility ("hidden")));
761638Srgrimes
771638Srgrimesunsigned int
781638Srgrimes__canonicalize_funcptr_for_compare (fptr_t fptr)
791638Srgrimes{
801638Srgrimes  static unsigned int fixup_plabel[2] __attribute__((used));
811638Srgrimes  fixup_t fixup;
821638Srgrimes  volatile unsigned int *plabel;
831638Srgrimes  unsigned int *got, *iptr, reloc_offset;
841638Srgrimes  int i;
851638Srgrimes
861638Srgrimes  /* -1 and page 0 are special.  -1 is used in crtend to mark the end of
871638Srgrimes     a list of function pointers.  Also return immediately if the plabel
881638Srgrimes     bit is not set in the function pointer.  In this case, the function
891638Srgrimes     pointer points directly to the function.  */
901638Srgrimes  if ((int) fptr == -1 || (unsigned int) fptr < 4096 || !((int) fptr & 2))
911638Srgrimes    return (unsigned int) fptr;
921638Srgrimes
931638Srgrimes  /* The function pointer points to a function descriptor (plabel).  If
941638Srgrimes     the plabel hasn't been resolved, the first word of the plabel points
951638Srgrimes     to the entry of the PLT stub just before the global offset table.
9622818Swosch     The second word in the plabel contains the relocation offset for the
971638Srgrimes     function.  */
981638Srgrimes  plabel = (volatile unsigned int *) ((unsigned int) fptr & ~3);
991638Srgrimes  if (!_dl_read_access_allowed ((unsigned int)plabel))
1001638Srgrimes    return (unsigned int) fptr;
1011638Srgrimes
1021638Srgrimes  /* Load first word of candidate descriptor.  It should be a pointer
1031638Srgrimes     with word alignment and point to memory that can be read.  */
1041638Srgrimes  got = (unsigned int *) plabel[0];
1051638Srgrimes  if (((unsigned int) got & 3) != 0
1061638Srgrimes      || !_dl_read_access_allowed ((unsigned int)got))
1071638Srgrimes    return (unsigned int) fptr;
1081638Srgrimes
1091638Srgrimes  /* We need to load the relocation offset before the function address.  */
1101638Srgrimes  reloc_offset = plabel[1];
1111638Srgrimes  __sync_synchronize();
1121638Srgrimes  got = (unsigned int *) (plabel[0] + GOT_FROM_PLT_STUB);
1131638Srgrimes
1141638Srgrimes  /* Return the address of the function if the plabel has been resolved.  */
1151638Srgrimes  if (got !=  &_GLOBAL_OFFSET_TABLE_)
1161638Srgrimes    return plabel[0];
1171638Srgrimes
1181638Srgrimes  /* Find the first "bl" branch in the offset search list.  This is a
1191638Srgrimes     call to _dl_fixup or a magic branch to fixup at the beginning of the
1201638Srgrimes     trampoline template.  The fixup function does the actual runtime
1211638Srgrimes     resolution of function descriptors.  We only look for "bl" branches
1221638Srgrimes     with a 17-bit pc-relative displacement.  */
1231638Srgrimes  for (i = 0; i < NOFFSETS; i++)
1241638Srgrimes    {
1251638Srgrimes      iptr = (unsigned int *) (got[-2] + fixup_branch_offset[i]);
1261638Srgrimes      if ((*iptr & 0xfc00e000) == 0xe8000000)
1271638Srgrimes	break;
1281638Srgrimes    }
1291638Srgrimes
1301638Srgrimes  /* This should not happen... */
1311638Srgrimes  if (i == NOFFSETS)
1321638Srgrimes    return ~0;
1331638Srgrimes
1341638Srgrimes  /* Extract the 17-bit displacement from the instruction.  */
1351638Srgrimes  iptr += SIGN_EXTEND (GET_FIELD (*iptr, 19, 28) |
1361638Srgrimes		       GET_FIELD (*iptr, 29, 29) << 10 |
1371638Srgrimes		       GET_FIELD (*iptr, 11, 15) << 11 |
1381638Srgrimes		       GET_FIELD (*iptr, 31, 31) << 16, 17);
1391638Srgrimes
1401638Srgrimes  /* Build a plabel for an indirect call to _dl_fixup.  */
1411638Srgrimes  fixup_plabel[0] = (unsigned int) iptr + 8;	/* address of fixup */
1421638Srgrimes  fixup_plabel[1] = got[-1];			/* ltp for fixup */
1431638Srgrimes  fixup = (fixup_t) ((int) fixup_plabel | 2);
1441638Srgrimes
1451638Srgrimes  /* Call fixup to resolve the function address.  got[1] contains the
1461638Srgrimes     link_map pointer and plabel[1] the relocation offset.  */
1471638Srgrimes  fixup ((struct link_map *) got[1], reloc_offset);
1481638Srgrimes
1491638Srgrimes  return plabel[0];
1501638Srgrimes}
1511638Srgrimes
1521638Srgrimes#pragma GCC diagnostic pop
1531638Srgrimes