fptr.c revision 1.4
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
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see 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
43static 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
50struct link_map;
51typedef int (*fptr_t) (void);
52typedef int (*fixup_t) (struct link_map *, unsigned int);
53extern unsigned int _GLOBAL_OFFSET_TABLE_;
54
55/* __canonicalize_funcptr_for_compare must be hidden so that it is not
56   placed in the dynamic symbol table.  Like millicode functions, it
57   must be linked into all binaries in order access the got table of
58   that binary.  However, we don't use the millicode calling convention
59   and the routine must be a normal function so that it can be compiled
60   as pic code.  */
61unsigned int __canonicalize_funcptr_for_compare (fptr_t)
62      __attribute__ ((visibility ("hidden")));
63
64unsigned int
65__canonicalize_funcptr_for_compare (fptr_t fptr)
66{
67  static unsigned int fixup_plabel[2] __attribute__((used));
68  fixup_t fixup;
69  unsigned int *got, *iptr, *plabel;
70  int i;
71
72  /* -1 and page 0 are special.  -1 is used in crtend to mark the end of
73     a list of function pointers.  Also return immediately if the plabel
74     bit is not set in the function pointer.  In this case, the function
75     pointer points directly to the function.  */
76  if ((int) fptr == -1 || (unsigned int) fptr < 4096 || !((int) fptr & 2))
77    return (unsigned int) fptr;
78
79  /* The function pointer points to a function descriptor (plabel).  If
80     the plabel hasn't been resolved, the first word of the plabel points
81     to the entry of the PLT stub just before the global offset table.
82     The second word in the plabel contains the relocation offset for the
83     function.  */
84  plabel = (unsigned int *) ((unsigned int) fptr & ~3);
85  got = (unsigned int *) (plabel[0] + GOT_FROM_PLT_STUB);
86
87  /* Return the address of the function if the plabel has been resolved.  */
88  if (got !=  &_GLOBAL_OFFSET_TABLE_)
89    return plabel[0];
90
91  /* Find the first "bl" branch in the offset search list.  This is a
92     call to _dl_fixup or a magic branch to fixup at the beginning of the
93     trampoline template.  The fixup function does the actual runtime
94     resolution of function descriptors.  We only look for "bl" branches
95     with a 17-bit pc-relative displacement.  */
96  for (i = 0; i < NOFFSETS; i++)
97    {
98      iptr = (unsigned int *) (got[-2] + fixup_branch_offset[i]);
99      if ((*iptr & 0xfc00e000) == 0xe8000000)
100	break;
101    }
102
103  /* This should not happen... */
104  if (i == NOFFSETS)
105    return ~0;
106
107  /* Extract the 17-bit displacement from the instruction.  */
108  iptr += SIGN_EXTEND (GET_FIELD (*iptr, 19, 28) |
109		       GET_FIELD (*iptr, 29, 29) << 10 |
110		       GET_FIELD (*iptr, 11, 15) << 11 |
111		       GET_FIELD (*iptr, 31, 31) << 16, 17);
112
113  /* Build a plabel for an indirect call to _dl_fixup.  */
114  fixup_plabel[0] = (unsigned int) iptr + 8;	/* address of fixup */
115  fixup_plabel[1] = got[-1];			/* ltp for fixup */
116  fixup = (fixup_t) ((int) fixup_plabel | 3);
117
118  /* Call fixup to resolve the function address.  got[1] contains the
119     link_map pointer and plabel[1] the relocation offset.  */
120  fixup ((struct link_map *) got[1], plabel[1]);
121
122  return plabel[0];
123}
124