fptr.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /* Subroutine for function pointer canonicalization on PA-RISC with ELF32.
  2. Copyright (C) 2002-2022 Free Software Foundation, Inc.
  3. Contributed by John David Anglin (dave.anglin@nrc.ca).
  4. This file is part of GCC.
  5. GCC is free software; you can redistribute it and/or modify it under
  6. the terms of the GNU General Public License as published by the Free
  7. Software Foundation; either version 3, or (at your option) any later
  8. version.
  9. GCC is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. for more details.
  13. Under Section 7 of GPL version 3, you are granted additional
  14. permissions described in the GCC Runtime Library Exception, version
  15. 3.1, as published by the Free Software Foundation.
  16. You should have received a copy of the GNU General Public License and
  17. a copy of the GCC Runtime Library Exception along with this program;
  18. see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  19. <http://www.gnu.org/licenses/>. */
  20. /* WARNING: The code is this function depends on internal and undocumented
  21. details of the GNU linker and dynamic loader as implemented for parisc
  22. linux. */
  23. /* This MUST match the defines sysdeps/hppa/dl-machine.h and
  24. bfd/elf32-hppa.c. */
  25. #define GOT_FROM_PLT_STUB (4*4)
  26. /* List of byte offsets in _dl_runtime_resolve to search for "bl" branches.
  27. The first "bl" branch instruction found MUST be a call to fixup. See
  28. the define for TRAMPOLINE_TEMPLATE in sysdeps/hppa/dl-machine.h. If
  29. the trampoline template is changed, the list must be appropriately
  30. updated. The offset of -4 allows for a magic branch at the start of
  31. the template should it be necessary to change the current branch
  32. position. */
  33. #define NOFFSETS 2
  34. static int fixup_branch_offset[NOFFSETS] = { -4, 32 };
  35. #define GET_FIELD(X, FROM, TO) \
  36. ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1))
  37. #define SIGN_EXTEND(VAL,BITS) \
  38. ((int) ((VAL) >> ((BITS) - 1) ? ((unsigned)(-1) << (BITS)) | (VAL) : (VAL)))
  39. struct link_map;
  40. typedef int (*fptr_t) (void);
  41. typedef int (*fixup_t) (struct link_map *, unsigned int);
  42. extern unsigned int _GLOBAL_OFFSET_TABLE_;
  43. static inline int
  44. _dl_read_access_allowed (unsigned int addr)
  45. {
  46. int result;
  47. asm ("proberi (%1),3,%0" : "=r" (result) : "r" (addr) : );
  48. return result;
  49. }
  50. #pragma GCC diagnostic push
  51. #pragma GCC diagnostic ignored "-Warray-bounds"
  52. /* __canonicalize_funcptr_for_compare must be hidden so that it is not
  53. placed in the dynamic symbol table. Like millicode functions, it
  54. must be linked into all binaries in order access the got table of
  55. that binary. However, we don't use the millicode calling convention
  56. and the routine must be a normal function so that it can be compiled
  57. as pic code. */
  58. unsigned int __canonicalize_funcptr_for_compare (fptr_t)
  59. __attribute__ ((visibility ("hidden")));
  60. unsigned int
  61. __canonicalize_funcptr_for_compare (fptr_t fptr)
  62. {
  63. static unsigned int fixup_plabel[2] __attribute__((used));
  64. fixup_t fixup;
  65. volatile unsigned int *plabel;
  66. unsigned int *got, *iptr, reloc_offset;
  67. int i;
  68. /* -1 and page 0 are special. -1 is used in crtend to mark the end of
  69. a list of function pointers. Also return immediately if the plabel
  70. bit is not set in the function pointer. In this case, the function
  71. pointer points directly to the function. */
  72. if ((int) fptr == -1 || (unsigned int) fptr < 4096 || !((int) fptr & 2))
  73. return (unsigned int) fptr;
  74. /* The function pointer points to a function descriptor (plabel). If
  75. the plabel hasn't been resolved, the first word of the plabel points
  76. to the entry of the PLT stub just before the global offset table.
  77. The second word in the plabel contains the relocation offset for the
  78. function. */
  79. plabel = (volatile unsigned int *) ((unsigned int) fptr & ~3);
  80. if (!_dl_read_access_allowed ((unsigned int)plabel))
  81. return (unsigned int) fptr;
  82. /* Load first word of candidate descriptor. It should be a pointer
  83. with word alignment and point to memory that can be read. */
  84. got = (unsigned int *) plabel[0];
  85. if (((unsigned int) got & 3) != 0
  86. || !_dl_read_access_allowed ((unsigned int)got))
  87. return (unsigned int) fptr;
  88. /* We need to load the relocation offset before the function address. */
  89. reloc_offset = plabel[1];
  90. __sync_synchronize();
  91. got = (unsigned int *) (plabel[0] + GOT_FROM_PLT_STUB);
  92. /* Return the address of the function if the plabel has been resolved. */
  93. if (got != &_GLOBAL_OFFSET_TABLE_)
  94. return plabel[0];
  95. /* Find the first "bl" branch in the offset search list. This is a
  96. call to _dl_fixup or a magic branch to fixup at the beginning of the
  97. trampoline template. The fixup function does the actual runtime
  98. resolution of function descriptors. We only look for "bl" branches
  99. with a 17-bit pc-relative displacement. */
  100. for (i = 0; i < NOFFSETS; i++)
  101. {
  102. iptr = (unsigned int *) (got[-2] + fixup_branch_offset[i]);
  103. if ((*iptr & 0xfc00e000) == 0xe8000000)
  104. break;
  105. }
  106. /* This should not happen... */
  107. if (i == NOFFSETS)
  108. return ~0;
  109. /* Extract the 17-bit displacement from the instruction. */
  110. iptr += SIGN_EXTEND (GET_FIELD (*iptr, 19, 28) |
  111. GET_FIELD (*iptr, 29, 29) << 10 |
  112. GET_FIELD (*iptr, 11, 15) << 11 |
  113. GET_FIELD (*iptr, 31, 31) << 16, 17);
  114. /* Build a plabel for an indirect call to _dl_fixup. */
  115. fixup_plabel[0] = (unsigned int) iptr + 8; /* address of fixup */
  116. fixup_plabel[1] = got[-1]; /* ltp for fixup */
  117. fixup = (fixup_t) ((int) fixup_plabel | 2);
  118. /* Call fixup to resolve the function address. got[1] contains the
  119. link_map pointer and plabel[1] the relocation offset. */
  120. fixup ((struct link_map *) got[1], reloc_offset);
  121. return plabel[0];
  122. }
  123. #pragma GCC diagnostic pop