]> 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 92596acf996725eda2939b838091317fea54e507..42a2539e6417907c5fc4ec4dd1be199deca81e16 100644 (file)
@@ -1,5 +1,5 @@
 /* On-demand PLT fixup for shared objects.
-   Copyright (C) 1995-2016 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
@@ -71,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;
@@ -123,14 +124,13 @@ _dl_fixup (
         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;
     }
 
@@ -145,7 +145,7 @@ _dl_fixup (
   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);
 }
 
 #ifndef PROF
@@ -183,10 +183,36 @@ _dl_profile_fixup (
   /* 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_index];
-  DL_FIXUP_VALUE_TYPE *resultp = &reloc_result->addr;
 
-  DL_FIXUP_VALUE_TYPE value = *resultp;
-  if (DL_FIXUP_VALUE_CODE_ADDR (value) == 0)
+ /* 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);
+
+  if (init == 0)
     {
       /* This is the first time we have to relocate this object.  */
       const ElfW(Sym) *const symtab
@@ -240,9 +266,7 @@ _dl_profile_fixup (
             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)
@@ -253,7 +277,7 @@ _dl_profile_fixup (
        {
          /* 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))
@@ -348,19 +372,31 @@ _dl_profile_fixup (
 
       /* Store the result for later runs.  */
       if (__glibc_likely (! GLRO(dl_bind_not)))
-       *resultp = value;
+       {
+         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);