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