1 /* Subroutine for function pointer canonicalization on PA-RISC with ELF32.
2    Copyright (C) 2002-2016 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
_dl_read_access_allowed(unsigned int * addr)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 /* __canonicalize_funcptr_for_compare must be hidden so that it is not
66    placed in the dynamic symbol table.  Like millicode functions, it
67    must be linked into all binaries in order access the got table of
68    that binary.  However, we don't use the millicode calling convention
69    and the routine must be a normal function so that it can be compiled
70    as pic code.  */
71 unsigned int __canonicalize_funcptr_for_compare (fptr_t)
72       __attribute__ ((visibility ("hidden")));
73 
74 unsigned int
__canonicalize_funcptr_for_compare(fptr_t fptr)75 __canonicalize_funcptr_for_compare (fptr_t fptr)
76 {
77   static unsigned int fixup_plabel[2] __attribute__((used));
78   fixup_t fixup;
79   unsigned int *got, *iptr, *plabel;
80   int i;
81 
82   /* -1 and page 0 are special.  -1 is used in crtend to mark the end of
83      a list of function pointers.  Also return immediately if the plabel
84      bit is not set in the function pointer.  In this case, the function
85      pointer points directly to the function.  */
86   if ((int) fptr == -1 || (unsigned int) fptr < 4096 || !((int) fptr & 2))
87     return (unsigned int) fptr;
88 
89   /* The function pointer points to a function descriptor (plabel).  If
90      the plabel hasn't been resolved, the first word of the plabel points
91      to the entry of the PLT stub just before the global offset table.
92      The second word in the plabel contains the relocation offset for the
93      function.  */
94   plabel = (unsigned int *) ((unsigned int) fptr & ~3);
95   if (!_dl_read_access_allowed (plabel))
96     return (unsigned int) fptr;
97 
98   /* Load first word of candidate descriptor.  It should be a pointer
99      with word alignment and point to memory that can be read.  */
100   got = (unsigned int *) plabel[0];
101   if (((unsigned int) got & 3) != 0
102       || !_dl_read_access_allowed (got))
103     return (unsigned int) fptr;
104 
105   got = (unsigned int *) (plabel[0] + GOT_FROM_PLT_STUB);
106 
107   /* Return the address of the function if the plabel has been resolved.  */
108   if (got !=  &_GLOBAL_OFFSET_TABLE_)
109     return plabel[0];
110 
111   /* Find the first "bl" branch in the offset search list.  This is a
112      call to _dl_fixup or a magic branch to fixup at the beginning of the
113      trampoline template.  The fixup function does the actual runtime
114      resolution of function descriptors.  We only look for "bl" branches
115      with a 17-bit pc-relative displacement.  */
116   for (i = 0; i < NOFFSETS; i++)
117     {
118       iptr = (unsigned int *) (got[-2] + fixup_branch_offset[i]);
119       if ((*iptr & 0xfc00e000) == 0xe8000000)
120 	break;
121     }
122 
123   /* This should not happen... */
124   if (i == NOFFSETS)
125     return ~0;
126 
127   /* Extract the 17-bit displacement from the instruction.  */
128   iptr += SIGN_EXTEND (GET_FIELD (*iptr, 19, 28) |
129 		       GET_FIELD (*iptr, 29, 29) << 10 |
130 		       GET_FIELD (*iptr, 11, 15) << 11 |
131 		       GET_FIELD (*iptr, 31, 31) << 16, 17);
132 
133   /* Build a plabel for an indirect call to _dl_fixup.  */
134   fixup_plabel[0] = (unsigned int) iptr + 8;	/* address of fixup */
135   fixup_plabel[1] = got[-1];			/* ltp for fixup */
136   fixup = (fixup_t) ((int) fixup_plabel | 3);
137 
138   /* Call fixup to resolve the function address.  got[1] contains the
139      link_map pointer and plabel[1] the relocation offset.  */
140   fixup ((struct link_map *) got[1], plabel[1]);
141 
142   return plabel[0];
143 }
144