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