1 |
148 |
jeremybenn |
/* On-demand PLT fixup for shared objects.
|
2 |
|
|
Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc.
|
3 |
|
|
This file is part of the GNU C Library.
|
4 |
|
|
|
5 |
|
|
The GNU C Library is free software; you can redistribute it and/or
|
6 |
|
|
modify it under the terms of the GNU Lesser General Public
|
7 |
|
|
License as published by the Free Software Foundation; either
|
8 |
|
|
version 2.1 of the License, or (at your option) any later version.
|
9 |
|
|
|
10 |
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
11 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13 |
|
|
Lesser General Public License for more details.
|
14 |
|
|
|
15 |
|
|
You should have received a copy of the GNU Lesser General Public
|
16 |
|
|
License along with the GNU C Library; if not, write to the Free
|
17 |
|
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
18 |
|
|
02111-1307 USA. */
|
19 |
|
|
|
20 |
|
|
#include <alloca.h>
|
21 |
|
|
#include <stdlib.h>
|
22 |
|
|
#include <unistd.h>
|
23 |
|
|
#include <ldsodefs.h>
|
24 |
|
|
#include "dynamic-link.h"
|
25 |
|
|
|
26 |
|
|
#ifndef __attribute_used__
|
27 |
|
|
#define __attribute_used__
|
28 |
|
|
#endif
|
29 |
|
|
|
30 |
|
|
#if !defined ELF_MACHINE_NO_RELA || ELF_MACHINE_NO_REL
|
31 |
|
|
# define PLTREL ElfW(Rela)
|
32 |
|
|
#else
|
33 |
|
|
# define PLTREL ElfW(Rel)
|
34 |
|
|
#endif
|
35 |
|
|
|
36 |
|
|
#ifndef VERSYMIDX
|
37 |
|
|
# define VERSYMIDX(sym) (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
|
38 |
|
|
#endif
|
39 |
|
|
|
40 |
|
|
|
41 |
|
|
/* This function is called through a special trampoline from the PLT the
|
42 |
|
|
first time each PLT entry is called. We must perform the relocation
|
43 |
|
|
specified in the PLT of the given shared object, and return the resolved
|
44 |
|
|
function address to the trampoline, which will restart the original call
|
45 |
|
|
to that address. Future calls will bounce directly from the PLT to the
|
46 |
|
|
function. */
|
47 |
|
|
|
48 |
|
|
#ifndef ELF_MACHINE_NO_PLT
|
49 |
|
|
static ElfW(Addr) __attribute__ ((regparm (2), used))
|
50 |
|
|
fixup (
|
51 |
|
|
# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
|
52 |
|
|
ELF_MACHINE_RUNTIME_FIXUP_ARGS,
|
53 |
|
|
# endif
|
54 |
|
|
/* GKM FIXME: Fix trampoline to pass bounds so we can do
|
55 |
|
|
without the `__unbounded' qualifier. */
|
56 |
|
|
struct link_map *__unbounded l, ElfW(Word) reloc_offset)
|
57 |
|
|
{
|
58 |
|
|
const ElfW(Sym) *const symtab
|
59 |
|
|
= (const void *) D_PTR (l, l_info[DT_SYMTAB]);
|
60 |
|
|
const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
|
61 |
|
|
|
62 |
|
|
const PLTREL *const reloc
|
63 |
|
|
= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
|
64 |
|
|
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
|
65 |
|
|
void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
|
66 |
|
|
lookup_t result;
|
67 |
|
|
ElfW(Addr) value;
|
68 |
|
|
|
69 |
|
|
/* The use of `alloca' here looks ridiculous but it helps. The goal is
|
70 |
|
|
to prevent the function from being inlined and thus optimized out.
|
71 |
|
|
There is no official way to do this so we use this trick. gcc never
|
72 |
|
|
inlines functions which use `alloca'. */
|
73 |
|
|
alloca (sizeof (int));
|
74 |
|
|
|
75 |
|
|
/* Sanity check that we're really looking at a PLT relocation. */
|
76 |
|
|
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
|
77 |
|
|
|
78 |
|
|
/* Look up the target symbol. If the normal lookup rules are not
|
79 |
|
|
used don't look in the global scope. */
|
80 |
|
|
if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
|
81 |
|
|
{
|
82 |
|
|
switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
|
83 |
|
|
{
|
84 |
|
|
default:
|
85 |
|
|
{
|
86 |
|
|
const ElfW(Half) *vernum =
|
87 |
|
|
(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
|
88 |
|
|
ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)];
|
89 |
|
|
const struct r_found_version *version = &l->l_versions[ndx];
|
90 |
|
|
|
91 |
|
|
if (version->hash != 0)
|
92 |
|
|
{
|
93 |
|
|
result = _dl_lookup_versioned_symbol (strtab + sym->st_name,
|
94 |
|
|
l, &sym, l->l_scope,
|
95 |
|
|
version,
|
96 |
|
|
ELF_RTYPE_CLASS_PLT, 0);
|
97 |
|
|
break;
|
98 |
|
|
}
|
99 |
|
|
}
|
100 |
|
|
case 0:
|
101 |
|
|
result = _dl_lookup_symbol (strtab + sym->st_name, l, &sym,
|
102 |
|
|
l->l_scope, ELF_RTYPE_CLASS_PLT, 0);
|
103 |
|
|
}
|
104 |
|
|
|
105 |
|
|
/* Currently result contains the base load address (or link map)
|
106 |
|
|
of the object that defines sym. Now add in the symbol
|
107 |
|
|
offset. */
|
108 |
|
|
value = (sym ? LOOKUP_VALUE_ADDRESS (result) + sym->st_value : 0);
|
109 |
|
|
}
|
110 |
|
|
else
|
111 |
|
|
{
|
112 |
|
|
/* We already found the symbol. The module (and therefore its load
|
113 |
|
|
address) is also known. */
|
114 |
|
|
value = l->l_addr + sym->st_value;
|
115 |
|
|
#ifdef DL_LOOKUP_RETURNS_MAP
|
116 |
|
|
result = l;
|
117 |
|
|
#endif
|
118 |
|
|
}
|
119 |
|
|
|
120 |
|
|
/* And now perhaps the relocation addend. */
|
121 |
|
|
value = elf_machine_plt_value (l, reloc, value);
|
122 |
|
|
|
123 |
|
|
/* Finally, fix up the plt itself. */
|
124 |
|
|
if (__builtin_expect (_dl_bind_not, 0))
|
125 |
|
|
return value;
|
126 |
|
|
|
127 |
|
|
return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
|
128 |
|
|
}
|
129 |
|
|
#endif
|
130 |
|
|
|
131 |
|
|
#if !defined PROF && !defined ELF_MACHINE_NO_PLT && !__BOUNDED_POINTERS__
|
132 |
|
|
|
133 |
|
|
static ElfW(Addr) __attribute__ ((regparm (3), used))
|
134 |
|
|
profile_fixup (
|
135 |
|
|
#ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
|
136 |
|
|
ELF_MACHINE_RUNTIME_FIXUP_ARGS,
|
137 |
|
|
#endif
|
138 |
|
|
struct link_map *l, ElfW(Word) reloc_offset, ElfW(Addr) retaddr)
|
139 |
|
|
{
|
140 |
|
|
void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = _dl_mcount;
|
141 |
|
|
ElfW(Addr) *resultp;
|
142 |
|
|
lookup_t result;
|
143 |
|
|
ElfW(Addr) value;
|
144 |
|
|
|
145 |
|
|
/* The use of `alloca' here looks ridiculous but it helps. The goal is
|
146 |
|
|
to prevent the function from being inlined, and thus optimized out.
|
147 |
|
|
There is no official way to do this so we use this trick. gcc never
|
148 |
|
|
inlines functions which use `alloca'. */
|
149 |
|
|
alloca (sizeof (int));
|
150 |
|
|
|
151 |
|
|
/* This is the address in the array where we store the result of previous
|
152 |
|
|
relocations. */
|
153 |
|
|
resultp = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)];
|
154 |
|
|
|
155 |
|
|
value = *resultp;
|
156 |
|
|
if (value == 0)
|
157 |
|
|
{
|
158 |
|
|
/* This is the first time we have to relocate this object. */
|
159 |
|
|
const ElfW(Sym) *const symtab
|
160 |
|
|
= (const void *) D_PTR (l, l_info[DT_SYMTAB]);
|
161 |
|
|
const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
|
162 |
|
|
|
163 |
|
|
const PLTREL *const reloc
|
164 |
|
|
= (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
|
165 |
|
|
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
|
166 |
|
|
|
167 |
|
|
/* Sanity check that we're really looking at a PLT relocation. */
|
168 |
|
|
assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);
|
169 |
|
|
|
170 |
|
|
/* Look up the target symbol. If the symbol is marked STV_PROTECTED
|
171 |
|
|
don't look in the global scope. */
|
172 |
|
|
if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
|
173 |
|
|
{
|
174 |
|
|
switch (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
|
175 |
|
|
{
|
176 |
|
|
default:
|
177 |
|
|
{
|
178 |
|
|
const ElfW(Half) *vernum =
|
179 |
|
|
(const void *) D_PTR (l,l_info[VERSYMIDX (DT_VERSYM)]);
|
180 |
|
|
ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)];
|
181 |
|
|
const struct r_found_version *version = &l->l_versions[ndx];
|
182 |
|
|
|
183 |
|
|
if (version->hash != 0)
|
184 |
|
|
{
|
185 |
|
|
result = _dl_lookup_versioned_symbol(strtab + sym->st_name,
|
186 |
|
|
l, &sym, l->l_scope,
|
187 |
|
|
version,
|
188 |
|
|
ELF_RTYPE_CLASS_PLT,
|
189 |
|
|
0);
|
190 |
|
|
break;
|
191 |
|
|
}
|
192 |
|
|
}
|
193 |
|
|
case 0:
|
194 |
|
|
result = _dl_lookup_symbol (strtab + sym->st_name, l, &sym,
|
195 |
|
|
l->l_scope, ELF_RTYPE_CLASS_PLT, 0);
|
196 |
|
|
}
|
197 |
|
|
|
198 |
|
|
/* Currently result contains the base load address (or link map)
|
199 |
|
|
of the object that defines sym. Now add in the symbol
|
200 |
|
|
offset. */
|
201 |
|
|
value = (sym ? LOOKUP_VALUE_ADDRESS (result) + sym->st_value : 0);
|
202 |
|
|
}
|
203 |
|
|
else
|
204 |
|
|
{
|
205 |
|
|
/* We already found the symbol. The module (and therefore its load
|
206 |
|
|
address) is also known. */
|
207 |
|
|
value = l->l_addr + sym->st_value;
|
208 |
|
|
#ifdef DL_LOOKUP_RETURNS_MAP
|
209 |
|
|
result = l;
|
210 |
|
|
#endif
|
211 |
|
|
}
|
212 |
|
|
/* And now perhaps the relocation addend. */
|
213 |
|
|
value = elf_machine_plt_value (l, reloc, value);
|
214 |
|
|
|
215 |
|
|
/* Store the result for later runs. */
|
216 |
|
|
if (__builtin_expect (! _dl_bind_not, 1))
|
217 |
|
|
*resultp = value;
|
218 |
|
|
}
|
219 |
|
|
|
220 |
|
|
(*mcount_fct) (retaddr, value);
|
221 |
|
|
|
222 |
|
|
return value;
|
223 |
|
|
}
|
224 |
|
|
|
225 |
|
|
#endif /* PROF && ELF_MACHINE_NO_PLT */
|
226 |
|
|
|
227 |
|
|
|
228 |
|
|
/* This macro is defined in dl-machine.h to define the entry point called
|
229 |
|
|
by the PLT. The `fixup' function above does the real work, but a little
|
230 |
|
|
more twiddling is needed to get the stack right and jump to the address
|
231 |
|
|
finally resolved. */
|
232 |
|
|
|
233 |
|
|
ELF_MACHINE_RUNTIME_TRAMPOLINE
|