]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - elf/dl-runtime.c
powerpc: Fix build of wcscpy with --disable-multi-arch
[thirdparty/glibc.git] / elf / dl-runtime.c
index 0488fab8d00c65f06b0afa924f296ec7d98a06fe..42a2539e6417907c5fc4ec4dd1be199deca81e16 100644 (file)
@@ -1,5 +1,5 @@
 /* On-demand PLT fixup for shared objects.
-   Copyright (C) 1995-2002,2003,2004,2005,2006 Free Software Foundation, Inc.
+   Copyright (C) 1995-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -13,9 +13,8 @@
    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, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #define IN_DL_RUNTIME 1                /* This can be tested in dl-machine.h.  */
 
 #include <ldsodefs.h>
 #include <sysdep-cancel.h>
 #include "dynamic-link.h"
+#include <tls.h>
+#include <dl-irel.h>
 
-#if (!defined ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \
+
+#if (!ELF_MACHINE_NO_RELA && !defined ELF_MACHINE_PLT_REL) \
     || ELF_MACHINE_NO_REL
 # define PLTREL  ElfW(Rela)
 #else
 # define PLTREL  ElfW(Rel)
 #endif
 
-#ifndef VERSYMIDX
-# define VERSYMIDX(sym)        (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (sym))
-#endif
-
 /* The fixup functions might have need special attributes.  If none
    are provided define the macro as empty.  */
 #ifndef ARCH_FIXUP_ATTRIBUTE
 # define ARCH_FIXUP_ATTRIBUTE
 #endif
 
+#ifndef reloc_offset
+# define reloc_offset reloc_arg
+# define reloc_index  reloc_arg / sizeof (PLTREL)
+#endif
+
+
 
 /* This function is called through a special trampoline from the PLT the
    first time each PLT entry is called.  We must perform the relocation
    to that address.  Future calls will bounce directly from the PLT to the
    function.  */
 
-#ifndef ELF_MACHINE_NO_PLT
 DL_FIXUP_VALUE_TYPE
-__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
+attribute_hidden __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
 _dl_fixup (
 # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
           ELF_MACHINE_RUNTIME_FIXUP_ARGS,
 # endif
-          /* GKM FIXME: Fix trampoline to pass bounds so we can do
-             without the `__unbounded' qualifier.  */
-          struct link_map *__unbounded l, ElfW(Word) reloc_offset)
+          struct link_map *l, ElfW(Word) reloc_arg)
 {
   const ElfW(Sym) *const symtab
     = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
@@ -70,6 +71,7 @@ _dl_fixup (
   const PLTREL *const reloc
     = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
   const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
+  const ElfW(Sym) *refsym = sym;
   void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
   lookup_t result;
   DL_FIXUP_VALUE_TYPE value;
@@ -93,63 +95,124 @@ _dl_fixup (
            version = NULL;
        }
 
-      if (l->l_type == lt_loaded && !SINGLE_THREAD_P)
-       __rtld_mrlock_lock (l->l_scope_lock);
+      /* We need to keep the scope around so do some locking.  This is
+        not necessary for objects which cannot be unloaded or when
+        we are not using any threads (yet).  */
+      int flags = DL_LOOKUP_ADD_DEPENDENCY;
+      if (!RTLD_SINGLE_THREAD_P)
+       {
+         THREAD_GSCOPE_SET_FLAG ();
+         flags |= DL_LOOKUP_GSCOPE_LOCK;
+       }
+
+#ifdef RTLD_ENABLE_FOREIGN_CALL
+      RTLD_ENABLE_FOREIGN_CALL;
+#endif
+
+      result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
+                                   version, ELF_RTYPE_CLASS_PLT, flags, NULL);
 
-      result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
-                                   l->l_scope, version, ELF_RTYPE_CLASS_PLT,
-                                   DL_LOOKUP_ADD_DEPENDENCY, NULL);
+      /* We are done with the global scope.  */
+      if (!RTLD_SINGLE_THREAD_P)
+       THREAD_GSCOPE_RESET_FLAG ();
 
-      if (l->l_type == lt_loaded && !SINGLE_THREAD_P)
-       __rtld_mrlock_unlock (l->l_scope_lock);
+#ifdef RTLD_FINALIZE_FOREIGN_CALL
+      RTLD_FINALIZE_FOREIGN_CALL;
+#endif
 
       /* Currently result contains the base load address (or link map)
         of the object that defines sym.  Now add in the symbol
         offset.  */
       value = DL_FIXUP_MAKE_VALUE (result,
-                                  sym ? (LOOKUP_VALUE_ADDRESS (result)
-                                         + sym->st_value) : 0);
+                                  SYMBOL_ADDRESS (result, sym, false));
     }
   else
     {
       /* We already found the symbol.  The module (and therefore its load
         address) is also known.  */
-      value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value);
+      value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, sym, true));
       result = l;
     }
 
   /* And now perhaps the relocation addend.  */
   value = elf_machine_plt_value (l, reloc, value);
 
+  if (sym != NULL
+      && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
+    value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
+
   /* Finally, fix up the plt itself.  */
-  if (__builtin_expect (GLRO(dl_bind_not), 0))
+  if (__glibc_unlikely (GLRO(dl_bind_not)))
     return value;
 
-  return elf_machine_fixup_plt (l, result, reloc, rel_addr, value);
+  return elf_machine_fixup_plt (l, result, refsym, sym, reloc, rel_addr, value);
 }
-#endif
-
-#if !defined PROF && !defined ELF_MACHINE_NO_PLT && !__BOUNDED_POINTERS__
 
+#ifndef PROF
 DL_FIXUP_VALUE_TYPE
 __attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
 _dl_profile_fixup (
 #ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
                   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
 #endif
-                  struct link_map *l, ElfW(Word) reloc_offset,
+                  struct link_map *l, ElfW(Word) reloc_arg,
                   ElfW(Addr) retaddr, void *regs, long int *framesizep)
 {
-  void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = INTUSE(_dl_mcount);
+  void (*mcount_fct) (ElfW(Addr), ElfW(Addr)) = _dl_mcount;
+
+  if (l->l_reloc_result == NULL)
+    {
+      /* BZ #14843: ELF_DYNAMIC_RELOCATE is called before l_reloc_result
+        is allocated.  We will get here if ELF_DYNAMIC_RELOCATE calls a
+        resolver function to resolve an IRELATIVE relocation and that
+        resolver calls a function that is not yet resolved (lazy).  For
+        example, the resolver in x86-64 libm.so calls __get_cpu_features
+        defined in libc.so.  Skip audit and resolve the external function
+        in this case.  */
+      *framesizep = -1;
+      return _dl_fixup (
+# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
+#  ifndef ELF_MACHINE_RUNTIME_FIXUP_PARAMS
+#   error Please define ELF_MACHINE_RUNTIME_FIXUP_PARAMS.
+#  endif
+                       ELF_MACHINE_RUNTIME_FIXUP_PARAMS,
+# endif
+                       l, reloc_arg);
+    }
 
   /* This is the address in the array where we store the result of previous
      relocations.  */
-  struct reloc_result *reloc_result
-    = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)];
-  DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr;
+  struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index];
+
+ /* CONCURRENCY NOTES:
+
+  Multiple threads may be calling the same PLT sequence and with
+  LD_AUDIT enabled they will be calling into _dl_profile_fixup to
+  update the reloc_result with the result of the lazy resolution.
+  The reloc_result guard variable is reloc_init, and we use
+  acquire/release loads and store to it to ensure that the results of
+  the structure are consistent with the loaded value of the guard.
+  This does not fix all of the data races that occur when two or more
+  threads read reloc_result->reloc_init with a value of zero and read
+  and write to that reloc_result concurrently.  The expectation is
+  generally that while this is a data race it works because the
+  threads write the same values.  Until the data races are fixed
+  there is a potential for problems to arise from these data races.
+  The reloc result updates should happen in parallel but there should
+  be an atomic RMW which does the final update to the real result
+  entry (see bug 23790).
+
+  The following code uses reloc_result->init set to 0 to indicate if it is
+  the first time this object is being relocated, otherwise 1 which
+  indicates the object has already been relocated.
+
+  Reading/Writing from/to reloc_result->reloc_init must not happen
+  before previous writes to reloc_result complete as they could
+  end-up with an incomplete struct.  */
+  DL_FIXUP_VALUE_TYPE value;
+  unsigned int init = atomic_load_acquire (&reloc_result->init);
 
-  DL_FIXUP_VALUE_TYPE value = *resultp;
-  if (DL_FIXUP_VALUE_CODE_ADDR (value) == 0)
+  if (init == 0)
     {
       /* This is the first time we have to relocate this object.  */
       const ElfW(Sym) *const symtab
@@ -181,30 +244,45 @@ _dl_profile_fixup (
                version = NULL;
            }
 
-         if (l->l_type == lt_loaded && !SINGLE_THREAD_P)
-           __rtld_mrlock_lock (l->l_scope_lock);
+         /* We need to keep the scope around so do some locking.  This is
+            not necessary for objects which cannot be unloaded or when
+            we are not using any threads (yet).  */
+         int flags = DL_LOOKUP_ADD_DEPENDENCY;
+         if (!RTLD_SINGLE_THREAD_P)
+           {
+             THREAD_GSCOPE_SET_FLAG ();
+             flags |= DL_LOOKUP_GSCOPE_LOCK;
+           }
 
-         result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, &defsym,
-                                       l->l_scope, version,
-                                       ELF_RTYPE_CLASS_PLT,
-                                       DL_LOOKUP_ADD_DEPENDENCY, NULL);
+         result = _dl_lookup_symbol_x (strtab + refsym->st_name, l,
+                                       &defsym, l->l_scope, version,
+                                       ELF_RTYPE_CLASS_PLT, flags, NULL);
 
-         if (l->l_type == lt_loaded && !SINGLE_THREAD_P)
-           __rtld_mrlock_unlock (l->l_scope_lock);
+         /* We are done with the global scope.  */
+         if (!RTLD_SINGLE_THREAD_P)
+           THREAD_GSCOPE_RESET_FLAG ();
 
          /* Currently result contains the base load address (or link map)
             of the object that defines sym.  Now add in the symbol
             offset.  */
          value = DL_FIXUP_MAKE_VALUE (result,
-                                      defsym != NULL
-                                      ? LOOKUP_VALUE_ADDRESS (result)
-                                        + defsym->st_value : 0);
+                                      SYMBOL_ADDRESS (result, defsym, false));
+
+         if (defsym != NULL
+             && __builtin_expect (ELFW(ST_TYPE) (defsym->st_info)
+                                  == STT_GNU_IFUNC, 0))
+           value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
        }
       else
        {
          /* We already found the symbol.  The module (and therefore its load
             address) is also known.  */
-         value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + refsym->st_value);
+         value = DL_FIXUP_MAKE_VALUE (l, SYMBOL_ADDRESS (l, refsym, true));
+
+         if (__builtin_expect (ELFW(ST_TYPE) (refsym->st_info)
+                               == STT_GNU_IFUNC, 0))
+           value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
+
          result = l;
        }
       /* And now perhaps the relocation addend.  */
@@ -227,7 +305,7 @@ _dl_profile_fixup (
             interested in auditing.  */
          if ((l->l_audit_any_plt | result->l_audit_any_plt) != 0)
            {
-             unsigned int altvalue = 0;
+             unsigned int flags = 0;
              struct audit_ifaces *afct = GLRO(dl_audit);
              /* Synthesize a symbol record where the st_value field is
                 the result.  */
@@ -250,7 +328,6 @@ _dl_profile_fixup (
                  if ((l->l_audit[cnt].bindflags & LA_FLG_BINDFROM) != 0
                      && (result->l_audit[cnt].bindflags & LA_FLG_BINDTO) != 0)
                    {
-                     unsigned int flags = altvalue;
                      if (afct->symbind != NULL)
                        {
                          uintptr_t new_value
@@ -261,7 +338,7 @@ _dl_profile_fixup (
                                             strtab2 + defsym->st_name);
                          if (new_value != (uintptr_t) sym.st_value)
                            {
-                             altvalue = LA_SYMB_ALTVALUE;
+                             flags |= LA_SYMB_ALTVALUE;
                              sym.st_value = new_value;
                            }
                        }
@@ -284,7 +361,7 @@ _dl_profile_fixup (
                  afct = afct->next;
                }
 
-             reloc_result->flags = altvalue;
+             reloc_result->flags = flags;
              value = DL_FIXUP_ADDR_VALUE (sym.st_value);
            }
          else
@@ -294,20 +371,32 @@ _dl_profile_fixup (
 #endif
 
       /* Store the result for later runs.  */
-      if (__builtin_expect (! GLRO(dl_bind_not), 1))
-       *resultp = value;
+      if (__glibc_likely (! GLRO(dl_bind_not)))
+       {
+         reloc_result->addr = value;
+         /* Guarantee all previous writes complete before
+            init is updated.  See CONCURRENCY NOTES earlier  */
+         atomic_store_release (&reloc_result->init, 1);
+       }
+      init = 1;
     }
+  else
+    value = reloc_result->addr;
 
   /* By default we do not call the pltexit function.  */
   long int framesize = -1;
 
+
 #ifdef SHARED
   /* Auditing checkpoint: report the PLT entering and allow the
      auditors to change the value.  */
-  if (DL_FIXUP_VALUE_CODE_ADDR (value) != 0 && GLRO(dl_naudit) > 0
+  if (GLRO(dl_naudit) > 0
       /* Don't do anything if no auditor wants to intercept this call.  */
       && (reloc_result->enterexit & LA_SYMB_NOPLTENTER) == 0)
     {
+      /* Sanity check:  DL_FIXUP_VALUE_CODE_ADDR (value) should have been
+        initialized earlier in this function or in another thread.  */
+      assert (DL_FIXUP_VALUE_CODE_ADDR (value) != 0);
       ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
                                                l_info[DT_SYMTAB])
                           + reloc_result->boundndx);
@@ -322,16 +411,15 @@ _dl_profile_fixup (
       const char *symname = strtab + sym.st_name;
 
       /* Keep track of overwritten addresses.  */
-      unsigned int altvalue = reloc_result->flags;
+      unsigned int flags = reloc_result->flags;
 
       struct audit_ifaces *afct = GLRO(dl_audit);
       for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
        {
-         if (afct->ARCH_LA_PLTENTER != NULL
+         if (afct->ARCH_LA_PLTENTER != NULL
              && (reloc_result->enterexit
                  & (LA_SYMB_NOPLTENTER << (2 * (cnt + 1)))) == 0)
            {
-             unsigned int flags = altvalue;
              long int new_framesize = -1;
              uintptr_t new_value
                = afct->ARCH_LA_PLTENTER (&sym, reloc_result->boundndx,
@@ -341,7 +429,7 @@ _dl_profile_fixup (
                                          &new_framesize);
              if (new_value != (uintptr_t) sym.st_value)
                {
-                 altvalue = LA_SYMB_ALTVALUE;
+                 flags |= LA_SYMB_ALTVALUE;
                  sym.st_value = new_value;
                }
 
@@ -381,13 +469,13 @@ _dl_profile_fixup (
   return value;
 }
 
-#endif /* PROF && ELF_MACHINE_NO_PLT */
+#endif /* PROF */
 
 
 #include <stdio.h>
 void
 ARCH_FIXUP_ATTRIBUTE
-_dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_offset,
+_dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
                  const void *inregs, void *outregs)
 {
 #ifdef SHARED
@@ -395,14 +483,14 @@ _dl_call_pltexit (struct link_map *l, ElfW(Word) reloc_offset,
      relocations.  */
   // XXX Maybe the bound information must be stored on the stack since
   // XXX with bind_not a new value could have been stored in the meantime.
-  struct reloc_result *reloc_result
-    = &l->l_reloc_result[reloc_offset / sizeof (PLTREL)];
+  struct reloc_result *reloc_result = &l->l_reloc_result[reloc_index];
   ElfW(Sym) *defsym = ((ElfW(Sym) *) D_PTR (reloc_result->bound,
                                            l_info[DT_SYMTAB])
                       + reloc_result->boundndx);
 
   /* Set up the sym parameter.  */
   ElfW(Sym) sym = *defsym;
+  sym.st_value = DL_FIXUP_VALUE_ADDR (reloc_result->addr);
 
   /* Get the symbol name.  */
   const char *strtab = (const void *) D_PTR (reloc_result->bound,