| 1 | /* On-demand PLT fixup for shared objects. |
| 2 | Copyright (C) 1995-2023 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, see |
| 17 | <https://www.gnu.org/licenses/>. */ |
| 18 | |
| 19 | #include <alloca.h> |
| 20 | #include <assert.h> |
| 21 | #include <stdlib.h> |
| 22 | #include <unistd.h> |
| 23 | #include <sys/param.h> |
| 24 | #include <ldsodefs.h> |
| 25 | #include <sysdep-cancel.h> |
| 26 | #include "dynamic-link.h" |
| 27 | #include <tls.h> |
| 28 | #include <dl-irel.h> |
| 29 | #include <dl-runtime.h> |
| 30 | |
| 31 | |
| 32 | /* This function is called through a special trampoline from the PLT the |
| 33 | first time each PLT entry is called. We must perform the relocation |
| 34 | specified in the PLT of the given shared object, and return the resolved |
| 35 | function address to the trampoline, which will restart the original call |
| 36 | to that address. Future calls will bounce directly from the PLT to the |
| 37 | function. */ |
| 38 | |
| 39 | DL_FIXUP_VALUE_TYPE |
| 40 | attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE |
| 41 | _dl_fixup ( |
| 42 | # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS |
| 43 | ELF_MACHINE_RUNTIME_FIXUP_ARGS, |
| 44 | # endif |
| 45 | struct link_map *l, ElfW(Word) reloc_arg) |
| 46 | { |
| 47 | const ElfW(Sym) *const symtab |
| 48 | = (const void *) D_PTR (l, l_info[DT_SYMTAB]); |
| 49 | const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); |
| 50 | |
| 51 | const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]); |
| 52 | |
| 53 | const PLTREL *const reloc |
| 54 | = (const void *) (D_PTR (l, l_info[DT_JMPREL]) |
| 55 | + reloc_offset (pltgot, reloc_arg)); |
| 56 | const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)]; |
| 57 | const ElfW(Sym) *refsym = sym; |
| 58 | void *const rel_addr = (void *)(l->l_addr + reloc->r_offset); |
| 59 | lookup_t result; |
| 60 | DL_FIXUP_VALUE_TYPE value; |
| 61 | |
| 62 | /* Sanity check that we're really looking at a PLT relocation. */ |
| 63 | assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); |
| 64 | |
| 65 | /* Look up the target symbol. If the normal lookup rules are not |
| 66 | used don't look in the global scope. */ |
| 67 | if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) |
| 68 | { |
| 69 | const struct r_found_version *version = NULL; |
| 70 | |
| 71 | if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) |
| 72 | { |
| 73 | const ElfW(Half) *vernum = |
| 74 | (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); |
| 75 | ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; |
| 76 | version = &l->l_versions[ndx]; |
| 77 | if (version->hash == 0) |
| 78 | version = NULL; |
| 79 | } |
| 80 | |
| 81 | /* We need to keep the scope around so do some locking. This is |
| 82 | not necessary for objects which cannot be unloaded or when |
| 83 | we are not using any threads (yet). */ |
| 84 | int flags = DL_LOOKUP_ADD_DEPENDENCY; |
| 85 | if (!RTLD_SINGLE_THREAD_P) |
| 86 | { |
| 87 | THREAD_GSCOPE_SET_FLAG (); |
| 88 | flags |= DL_LOOKUP_GSCOPE_LOCK; |
| 89 | } |
| 90 | |
| 91 | #ifdef RTLD_ENABLE_FOREIGN_CALL |
| 92 | RTLD_ENABLE_FOREIGN_CALL; |
| 93 | #endif |
| 94 | |
| 95 | result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, |
| 96 | version, ELF_RTYPE_CLASS_PLT, flags, NULL); |
| 97 | |
| 98 | /* We are done with the global scope. */ |
| 99 | if (!RTLD_SINGLE_THREAD_P) |
| 100 | THREAD_GSCOPE_RESET_FLAG (); |
| 101 | |
| 102 | #ifdef RTLD_FINALIZE_FOREIGN_CALL |
| 103 | RTLD_FINALIZE_FOREIGN_CALL; |
| 104 | #endif |
| 105 | |
| 106 | /* Currently result contains the base load address (or link map) |
| 107 | of the object that defines sym. Now add in the symbol |
| 108 | offset. */ |
| 109 | value = DL_FIXUP_MAKE_VALUE (result, |
| 110 | SYMBOL_ADDRESS (result, sym, false)); |
| 111 | } |
| 112 | else |
| 113 | { |
| 114 | /* We already found the symbol. The module (and therefore its load |
| 115 | address) is also known. */ |
| 116 | value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, sym, true)); |
| 117 | result = l; |
| 118 | } |
| 119 | |
| 120 | /* And now perhaps the relocation addend. */ |
| 121 | value = elf_machine_plt_value (l, reloc, value); |
| 122 | |
| 123 | if (sym != NULL |
| 124 | && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)) |
| 125 | value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); |
| 126 | |
| 127 | #ifdef SHARED |
| 128 | /* Auditing checkpoint: we have a new binding. Provide the auditing |
| 129 | libraries the possibility to change the value and tell us whether further |
| 130 | auditing is wanted. |
| 131 | The l_reloc_result is only allocated if there is an audit module which |
| 132 | provides a la_symbind. */ |
| 133 | if (l->l_reloc_result != NULL) |
| 134 | { |
| 135 | /* This is the address in the array where we store the result of previous |
| 136 | relocations. */ |
| 137 | struct reloc_result *reloc_result |
| 138 | = &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))]; |
| 139 | unsigned int init = atomic_load_acquire (&reloc_result->init); |
| 140 | if (init == 0) |
| 141 | { |
| 142 | _dl_audit_symbind (l, reloc_result, reloc, sym, &value, result, true); |
| 143 | |
| 144 | /* Store the result for later runs. */ |
| 145 | if (__glibc_likely (! GLRO(dl_bind_not))) |
| 146 | { |
| 147 | reloc_result->addr = value; |
| 148 | /* Guarantee all previous writes complete before init is |
| 149 | updated. See CONCURRENCY NOTES below. */ |
| 150 | atomic_store_release (&reloc_result->init, 1); |
| 151 | } |
| 152 | } |
| 153 | else |
| 154 | value = reloc_result->addr; |
| 155 | } |
| 156 | #endif |
| 157 | |
| 158 | /* Finally, fix up the plt itself. */ |
| 159 | if (__glibc_unlikely (GLRO(dl_bind_not))) |
| 160 | return value; |
| 161 | |
| 162 | return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value); |
| 163 | } |
| 164 | |
| 165 | #ifndef PROF |
| 166 | DL_FIXUP_VALUE_TYPE |
| 167 | __attribute ((noinline)) |
| 168 | DL_ARCH_FIXUP_ATTRIBUTE |
| 169 | _dl_profile_fixup ( |
| 170 | #ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS |
| 171 | ELF_MACHINE_RUNTIME_FIXUP_ARGS, |
| 172 | #endif |
| 173 | struct link_map *l, ElfW(Word) reloc_arg, |
| 174 | ElfW(Addr) retaddr, void *regs, long int *framesizep) |
| 175 | { |
| 176 | void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = _dl_mcount; |
| 177 | |
| 178 | if (l->l_reloc_result == NULL) |
| 179 | { |
| 180 | /* BZ #14843: ELF_DYNAMIC_RELOCATE is called before l_reloc_result |
| 181 | is allocated. We will get here if ELF_DYNAMIC_RELOCATE calls a |
| 182 | resolver function to resolve an IRELATIVE relocation and that |
| 183 | resolver calls a function that is not yet resolved (lazy). For |
| 184 | example, the resolver in x86-64 libm.so calls __get_cpu_features |
| 185 | defined in libc.so. Skip audit and resolve the external function |
| 186 | in this case. */ |
| 187 | *framesizep = -1; |
| 188 | return _dl_fixup ( |
| 189 | # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS |
| 190 | # ifndef ELF_MACHINE_RUNTIME_FIXUP_PARAMS |
| 191 | # error Please define ELF_MACHINE_RUNTIME_FIXUP_PARAMS. |
| 192 | # endif |
| 193 | ELF_MACHINE_RUNTIME_FIXUP_PARAMS, |
| 194 | # endif |
| 195 | l, reloc_arg); |
| 196 | } |
| 197 | |
| 198 | const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]); |
| 199 | |
| 200 | /* This is the address in the array where we store the result of previous |
| 201 | relocations. */ |
| 202 | struct reloc_result *reloc_result |
| 203 | = &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))]; |
| 204 | |
| 205 | /* CONCURRENCY NOTES: |
| 206 | |
| 207 | Multiple threads may be calling the same PLT sequence and with |
| 208 | LD_AUDIT enabled they will be calling into _dl_profile_fixup to |
| 209 | update the reloc_result with the result of the lazy resolution. |
| 210 | The reloc_result guard variable is reloc_init, and we use |
| 211 | acquire/release loads and store to it to ensure that the results of |
| 212 | the structure are consistent with the loaded value of the guard. |
| 213 | This does not fix all of the data races that occur when two or more |
| 214 | threads read reloc_result->reloc_init with a value of zero and read |
| 215 | and write to that reloc_result concurrently. The expectation is |
| 216 | generally that while this is a data race it works because the |
| 217 | threads write the same values. Until the data races are fixed |
| 218 | there is a potential for problems to arise from these data races. |
| 219 | The reloc result updates should happen in parallel but there should |
| 220 | be an atomic RMW which does the final update to the real result |
| 221 | entry (see bug 23790). |
| 222 | |
| 223 | The following code uses reloc_result->init set to 0 to indicate if it is |
| 224 | the first time this object is being relocated, otherwise 1 which |
| 225 | indicates the object has already been relocated. |
| 226 | |
| 227 | Reading/Writing from/to reloc_result->reloc_init must not happen |
| 228 | before previous writes to reloc_result complete as they could |
| 229 | end-up with an incomplete struct. */ |
| 230 | DL_FIXUP_VALUE_TYPE value; |
| 231 | unsigned int init = atomic_load_acquire (&reloc_result->init); |
| 232 | |
| 233 | if (init == 0) |
| 234 | { |
| 235 | /* This is the first time we have to relocate this object. */ |
| 236 | const ElfW(Sym) *const symtab |
| 237 | = (const void *) D_PTR (l, l_info[DT_SYMTAB]); |
| 238 | const char *strtab = (const char *) D_PTR (l, l_info[DT_STRTAB]); |
| 239 | |
| 240 | const uintptr_t pltgot = (uintptr_t) D_PTR (l, l_info[DT_PLTGOT]); |
| 241 | |
| 242 | const PLTREL *const reloc |
| 243 | = (const void *) (D_PTR (l, l_info[DT_JMPREL]) |
| 244 | + reloc_offset (pltgot, reloc_arg)); |
| 245 | const ElfW(Sym) *refsym = &symtab[ELFW(R_SYM) (reloc->r_info)]; |
| 246 | const ElfW(Sym) *defsym = refsym; |
| 247 | lookup_t result; |
| 248 | |
| 249 | /* Sanity check that we're really looking at a PLT relocation. */ |
| 250 | assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT); |
| 251 | |
| 252 | /* Look up the target symbol. If the symbol is marked STV_PROTECTED |
| 253 | don't look in the global scope. */ |
| 254 | if (__builtin_expect (ELFW(ST_VISIBILITY) (refsym->st_other), 0) == 0) |
| 255 | { |
| 256 | const struct r_found_version *version = NULL; |
| 257 | |
| 258 | if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) |
| 259 | { |
| 260 | const ElfW(Half) *vernum = |
| 261 | (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); |
| 262 | ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; |
| 263 | version = &l->l_versions[ndx]; |
| 264 | if (version->hash == 0) |
| 265 | version = NULL; |
| 266 | } |
| 267 | |
| 268 | /* We need to keep the scope around so do some locking. This is |
| 269 | not necessary for objects which cannot be unloaded or when |
| 270 | we are not using any threads (yet). */ |
| 271 | int flags = DL_LOOKUP_ADD_DEPENDENCY; |
| 272 | if (!RTLD_SINGLE_THREAD_P) |
| 273 | { |
| 274 | THREAD_GSCOPE_SET_FLAG (); |
| 275 | flags |= DL_LOOKUP_GSCOPE_LOCK; |
| 276 | } |
| 277 | |
| 278 | result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, |
| 279 | &defsym, l->l_scope, version, |
| 280 | ELF_RTYPE_CLASS_PLT, flags, NULL); |
| 281 | |
| 282 | /* We are done with the global scope. */ |
| 283 | if (!RTLD_SINGLE_THREAD_P) |
| 284 | THREAD_GSCOPE_RESET_FLAG (); |
| 285 | |
| 286 | /* Currently result contains the base load address (or link map) |
| 287 | of the object that defines sym. Now add in the symbol |
| 288 | offset. */ |
| 289 | value = DL_FIXUP_MAKE_VALUE (result, |
| 290 | SYMBOL_ADDRESS (result, defsym, false)); |
| 291 | |
| 292 | if (defsym != NULL |
| 293 | && __builtin_expect (ELFW(ST_TYPE) (defsym->st_info) |
| 294 | == STT_GNU_IFUNC, 0)) |
| 295 | value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); |
| 296 | } |
| 297 | else |
| 298 | { |
| 299 | /* We already found the symbol. The module (and therefore its load |
| 300 | address) is also known. */ |
| 301 | value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, refsym, true)); |
| 302 | |
| 303 | if (__builtin_expect (ELFW(ST_TYPE) (refsym->st_info) |
| 304 | == STT_GNU_IFUNC, 0)) |
| 305 | value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value)); |
| 306 | |
| 307 | result = l; |
| 308 | } |
| 309 | /* And now perhaps the relocation addend. */ |
| 310 | value = elf_machine_plt_value (l, reloc, value); |
| 311 | |
| 312 | #ifdef SHARED |
| 313 | /* Auditing checkpoint: we have a new binding. Provide the |
| 314 | auditing libraries the possibility to change the value and |
| 315 | tell us whether further auditing is wanted. */ |
| 316 | if (defsym != NULL && GLRO(dl_naudit) > 0) |
| 317 | _dl_audit_symbind (l, reloc_result, reloc, defsym, &value, result, |
| 318 | true); |
| 319 | #endif |
| 320 | |
| 321 | /* Store the result for later runs. */ |
| 322 | if (__glibc_likely (! GLRO(dl_bind_not))) |
| 323 | { |
| 324 | reloc_result->addr = value; |
| 325 | /* Guarantee all previous writes complete before |
| 326 | init is updated. See CONCURRENCY NOTES earlier */ |
| 327 | atomic_store_release (&reloc_result->init, 1); |
| 328 | } |
| 329 | init = 1; |
| 330 | } |
| 331 | else |
| 332 | value = reloc_result->addr; |
| 333 | |
| 334 | /* By default we do not call the pltexit function. */ |
| 335 | long int framesize = -1; |
| 336 | |
| 337 | |
| 338 | #ifdef SHARED |
| 339 | /* Auditing checkpoint: report the PLT entering and allow the |
| 340 | auditors to change the value. */ |
| 341 | _dl_audit_pltenter (l, reloc_result, &value, regs, &framesize); |
| 342 | #endif |
| 343 | |
| 344 | /* Store the frame size information. */ |
| 345 | *framesizep = framesize; |
| 346 | |
| 347 | (*mcount_fct) (retaddr, DL_FIXUP_VALUE_CODE_ADDR (value)); |
| 348 | |
| 349 | return value; |
| 350 | } |
| 351 | |
| 352 | #endif /* PROF */ |
| 353 | |