diff options
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/x86_64/dl-ifunc-reloc.h | 91 | ||||
-rw-r--r-- | sysdeps/x86_64/dl-machine.h | 26 |
2 files changed, 99 insertions, 18 deletions
diff --git a/sysdeps/x86_64/dl-ifunc-reloc.h b/sysdeps/x86_64/dl-ifunc-reloc.h new file mode 100644 index 0000000000..bf436bf71c --- /dev/null +++ b/sysdeps/x86_64/dl-ifunc-reloc.h @@ -0,0 +1,91 @@ +/* IFUNC relocation processing, x86-64 version. + Copyright (C) 2001-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Andreas Jaeger <aj@suse.de>. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef DL_IFUNC_RELOC_H +#define DL_IFUNC_RELOC_H + +static inline void +_dl_ifunc_process_relocation (const struct dl_ifunc_relocation *ifunc, + struct link_map *sym_map) +{ + const ElfW(Rela) *reloc = ifunc->reloc; + const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info); + ElfW(Addr) *const reloc_addr = ifunc->reloc_addr; + + /* Special case: A relative IFUNC relocation does not have an + associated symbol. */ + if (r_type == R_X86_64_IRELATIVE) + { + ElfW(Addr) value = sym_map->l_addr + reloc->r_addend; + value = ((ElfW(Addr) (*) (void)) value) (); + *reloc_addr = value; + return; + } + + ElfW(Addr) value = (ElfW(Addr)) sym_map->l_addr + ifunc->ifunc_sym->st_value; + value = ((ElfW(Addr) (*) (void)) value) (); + + /* This switch statement needs to be kept in sync with the switch + statement in elf_machine_rela. */ + switch (r_type) + { + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + *reloc_addr = value + reloc->r_addend; + break; + + case R_X86_64_64: + /* value + r_addend may be > 0xffffffff and R_X86_64_64 + relocation updates the whole 64-bit entry. */ + *(Elf64_Addr *) reloc_addr = (Elf64_Addr) value + reloc->r_addend; + break; + case R_X86_64_32: + value += reloc->r_addend; + *(unsigned int *) reloc_addr = value; + + const char *fmt; + if (__glibc_unlikely (value > UINT_MAX)) + { + const char *strtab; + + fmt = "\ +%s: Symbol `%s' causes overflow in R_X86_64_32 relocation\n"; + print_err: + strtab = (const char *) D_PTR (sym_map, l_info[DT_STRTAB]); + + _dl_error_printf (fmt, RTLD_PROGNAME, + strtab + ifunc->ifunc_sym->st_name); + } + break; + case R_X86_64_PC32: + value += reloc->r_addend - (ElfW(Addr)) reloc_addr; + *(unsigned int *) reloc_addr = value; + if (__glibc_unlikely (value != (int) value)) + { + fmt = "\ +%s: Symbol `%s' causes overflow in R_X86_64_PC32 relocation\n"; + goto print_err; + } + break; + default: + _dl_reloc_bad_type (ifunc->reloc_map, r_type, 0); + } +} + +#endif /* DL_IFUNC_RELOC_H */ diff --git a/sysdeps/x86_64/dl-machine.h b/sysdeps/x86_64/dl-machine.h index 3e7ae22c67..02b6703b2e 100644 --- a/sysdeps/x86_64/dl-machine.h +++ b/sysdeps/x86_64/dl-machine.h @@ -27,6 +27,7 @@ #include <tls.h> #include <dl-tlsdesc.h> #include <cpu-features.c> +#include <dl-ifunc.h> /* Return nonzero iff ELF header is compatible with the running host. */ static inline int __attribute__ ((unused)) @@ -326,29 +327,20 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc, ElfW(Addr) value = (sym == NULL ? 0 : (ElfW(Addr)) sym_map->l_addr + sym->st_value); +# ifndef RTLD_BOOTSTRAP if (sym != NULL && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0) && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1) && __builtin_expect (!skip_ifunc, 1)) { -# ifndef RTLD_BOOTSTRAP - if (sym_map != map - && sym_map->l_type != lt_executable - && !sym_map->l_relocated) - { - const char *strtab - = (const char *) D_PTR (map, l_info[DT_STRTAB]); - _dl_fatal_printf ("\ -%s: Relink `%s' with `%s' for IFUNC symbol `%s'\n", - RTLD_PROGNAME, map->l_name, - sym_map->l_name, - strtab + refsym->st_name); - } -# endif - value = ((ElfW(Addr) (*) (void)) value) (); + _dl_ifunc_record_reloc (map, reloc, reloc_addr, sym_map, sym); + return; } +# endif /* !RTLD_BOOTSTRAP */ + /* This switch statement needs to be kept in sync with the + switch statement in _dl_ifunc_process_relocation. */ switch (r_type) { # ifndef RTLD_BOOTSTRAP @@ -528,9 +520,7 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc, break; # endif case R_X86_64_IRELATIVE: - value = map->l_addr + reloc->r_addend; - value = ((ElfW(Addr) (*) (void)) value) (); - *reloc_addr = value; + _dl_ifunc_record_reloc (map, reloc, reloc_addr, map, NULL); break; default: _dl_reloc_bad_type (map, r_type, 0); |