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