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