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