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