d8307d
commit 365624e2d2a342cdb693b4cc35d2312169959e28
d8307d
Author: Florian Weimer <fweimer@redhat.com>
d8307d
Date:   Fri Dec 13 10:18:24 2019 +0100
d8307d
d8307d
    dlopen: Fix issues related to NODELETE handling and relocations
d8307d
    
d8307d
    The assumption behind the assert in activate_nodelete was wrong:
d8307d
    
d8307d
    Inconsistency detected by ld.so: dl-open.c: 459: activate_nodelete:
d8307d
    Assertion `!imap->l_init_called || imap->l_type != lt_loaded' failed! (edit)
d8307d
    
d8307d
    It can happen that an already-loaded object that is in the local
d8307d
    scope is promoted to NODELETE status, via binding to a unique
d8307d
    symbol.
d8307d
    
d8307d
    Similarly, it is possible that such NODELETE promotion occurs to
d8307d
    an already-loaded object from the global scope.  This is why the
d8307d
    loop in activate_nodelete has to cover all objects in the namespace
d8307d
    of the new object.
d8307d
    
d8307d
    In do_lookup_unique, it could happen that the NODELETE status of
d8307d
    an already-loaded object was overwritten with a pending NODELETE
d8307d
    status.  As a result, if dlopen fails, this could cause a loss of
d8307d
    the NODELETE status of the affected object, eventually resulting
d8307d
    in an incorrect unload.
d8307d
    
d8307d
    Fixes commit f63b73814f74032c0e5d0a83300e3d864ef905e5 ("Remove all
d8307d
    loaded objects if dlopen fails, ignoring NODELETE [BZ #20839]").
d8307d
d8307d
diff --git a/elf/Makefile b/elf/Makefile
d8307d
index 467e810e784bb96d..16a3e8dcda19b4ba 100644
d8307d
--- a/elf/Makefile
d8307d
+++ b/elf/Makefile
d8307d
@@ -185,7 +185,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
d8307d
 	 tst-audit1 tst-audit2 tst-audit8 tst-audit9 \
d8307d
 	 tst-addr1 tst-thrlock \
d8307d
 	 tst-unique1 tst-unique2 $(if $(CXX),tst-unique3 tst-unique4 \
d8307d
-	 tst-nodelete) \
d8307d
+	 tst-nodelete tst-dlopen-nodelete-reloc) \
d8307d
 	 tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \
d8307d
 	 tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \
d8307d
 	 tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
d8307d
@@ -266,7 +266,24 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
d8307d
 		tst-auditmod9a tst-auditmod9b \
d8307d
 		$(if $(CXX),tst-unique3lib tst-unique3lib2 tst-unique4lib \
d8307d
 		  tst-nodelete-uniquemod tst-nodelete-rtldmod \
d8307d
-		  tst-nodelete-zmod) \
d8307d
+		  tst-nodelete-zmod \
d8307d
+                  tst-dlopen-nodelete-reloc-mod1 \
d8307d
+		  tst-dlopen-nodelete-reloc-mod2 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod3 \
d8307d
+		  tst-dlopen-nodelete-reloc-mod4 \
d8307d
+		  tst-dlopen-nodelete-reloc-mod5 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod6 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod7 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod8 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod9 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod10 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod11 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod12 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod13 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod14 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod15 \
d8307d
+	          tst-dlopen-nodelete-reloc-mod16 \
d8307d
+		  tst-dlopen-nodelete-reloc-mod17) \
d8307d
 		tst-initordera1 tst-initorderb1 \
d8307d
 		tst-initordera2 tst-initorderb2 \
d8307d
 		tst-initordera3 tst-initordera4 \
d8307d
@@ -1552,3 +1569,48 @@ $(objpfx)tst-dlopenfailmod1.so: \
d8307d
   $(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so
d8307d
 LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so
d8307d
 $(objpfx)tst-dlopenfailmod2.so: $(shared-thread-library)
d8307d
+
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc: $(libdl)
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc.out: \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod1.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod2.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod3.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod4.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod5.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod6.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod7.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod8.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod9.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod10.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod11.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod12.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod13.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod14.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod15.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod16.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod17.so
d8307d
+tst-dlopen-nodelete-reloc-mod2.so-no-z-defs = yes
d8307d
+LDFLAGS-tst-dlopen-nodelete-reloc-mod2.so = -Wl,-z,nodelete
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod4.so: \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod3.so
d8307d
+LDFLAGS-tst-dlopen-nodelete-reloc-mod4.so = -Wl,--no-as-needed
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod5.so: \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod4.so
d8307d
+LDFLAGS-tst-dlopen-nodelete-reloc-mod5.so = -Wl,-z,nodelete,--no-as-needed
d8307d
+tst-dlopen-nodelete-reloc-mod5.so-no-z-defs = yes
d8307d
+tst-dlopen-nodelete-reloc-mod7.so-no-z-defs = yes
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod8.so: $(libdl)
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod10.so: $(libdl)
d8307d
+tst-dlopen-nodelete-reloc-mod11.so-no-z-defs = yes
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod13.so: \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod12.so
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod15.so: \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod14.so
d8307d
+tst-dlopen-nodelete-reloc-mod16.so-no-z-defs = yes
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod16.so: \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod15.so
d8307d
+LDFLAGS-tst-dlopen-nodelete-reloc-mod16.so = -Wl,--no-as-needed
d8307d
+$(objpfx)tst-dlopen-nodelete-reloc-mod17.so: \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod15.so \
d8307d
+  $(objpfx)tst-dlopen-nodelete-reloc-mod16.so
d8307d
+LDFLAGS-tst-dlopen-nodelete-reloc-mod17.so = -Wl,--no-as-needed
d8307d
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
d8307d
index c5e5857fb1fe2808..35a3f96a6296294a 100644
d8307d
--- a/elf/dl-lookup.c
d8307d
+++ b/elf/dl-lookup.c
d8307d
@@ -311,12 +311,12 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
d8307d
       enter_unique_sym (entries, size,
d8307d
                         new_hash, strtab + sym->st_name, sym, map);
d8307d
 
d8307d
-      if (map->l_type == lt_loaded)
d8307d
+      if (map->l_type == lt_loaded
d8307d
+	  && map->l_nodelete == link_map_nodelete_inactive)
d8307d
 	{
d8307d
 	  /* Make sure we don't unload this object by
d8307d
 	     setting the appropriate flag.  */
d8307d
-	  if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)
d8307d
-	      && map->l_nodelete == link_map_nodelete_inactive)
d8307d
+	  if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS))
d8307d
 	    _dl_debug_printf ("\
d8307d
 marking %s [%lu] as NODELETE due to unique symbol\n",
d8307d
 			      map->l_name, map->l_ns);
d8307d
diff --git a/elf/dl-open.c b/elf/dl-open.c
d8307d
index e13968d4d7c4c83f..c7ed85b7ee99a296 100644
d8307d
--- a/elf/dl-open.c
d8307d
+++ b/elf/dl-open.c
d8307d
@@ -433,34 +433,21 @@ TLS generation counter wrapped!  Please report this."));
d8307d
    after dlopen failure is not possible, so that _dl_close can clean
d8307d
    up objects if necessary.  */
d8307d
 static void
d8307d
-activate_nodelete (struct link_map *new, int mode)
d8307d
+activate_nodelete (struct link_map *new)
d8307d
 {
d8307d
-  if (mode & RTLD_NODELETE || new->l_nodelete == link_map_nodelete_pending)
d8307d
-    {
d8307d
-      if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
d8307d
-	_dl_debug_printf ("activating NODELETE for %s [%lu]\n",
d8307d
-			  new->l_name, new->l_ns);
d8307d
-      new->l_nodelete = link_map_nodelete_active;
d8307d
-    }
d8307d
-
d8307d
-  for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
d8307d
-    {
d8307d
-      struct link_map *imap = new->l_searchlist.r_list[i];
d8307d
-      if (imap->l_nodelete == link_map_nodelete_pending)
d8307d
-	{
d8307d
-	  if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
d8307d
-	    _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
d8307d
-			      imap->l_name, imap->l_ns);
d8307d
-
d8307d
-	  /* Only new objects should have set
d8307d
-	     link_map_nodelete_pending.  Existing objects should not
d8307d
-	     have gained any new dependencies and therefore cannot
d8307d
-	     reach NODELETE status.  */
d8307d
-	  assert (!imap->l_init_called || imap->l_type != lt_loaded);
d8307d
+  /* It is necessary to traverse the entire namespace.  References to
d8307d
+     objects in the global scope and unique symbol bindings can force
d8307d
+     NODELETE status for objects outside the local scope.  */
d8307d
+  for (struct link_map *l = GL (dl_ns)[new->l_ns]._ns_loaded; l != NULL;
d8307d
+       l = l->l_next)
d8307d
+    if (l->l_nodelete == link_map_nodelete_pending)
d8307d
+      {
d8307d
+	if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
d8307d
+	  _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
d8307d
+			    l->l_name, l->l_ns);
d8307d
 
d8307d
-	  imap->l_nodelete = link_map_nodelete_active;
d8307d
-	}
d8307d
-     }
d8307d
+	l->l_nodelete = link_map_nodelete_active;
d8307d
+      }
d8307d
 }
d8307d
 
d8307d
 /* struct dl_init_args and call_dl_init are used to call _dl_init with
d8307d
@@ -718,7 +705,7 @@ dl_open_worker (void *a)
d8307d
      All memory allocations for new objects must have happened
d8307d
      before.  */
d8307d
 
d8307d
-  activate_nodelete (new, mode);
d8307d
+  activate_nodelete (new);
d8307d
 
d8307d
   /* Second stage after resize_scopes: Actually perform the scope
d8307d
      update.  After this, dlsym and lazy binding can bind to new
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod1.c b/elf/tst-dlopen-nodelete-reloc-mod1.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..397d60a2d5ea62d9
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod1.c
d8307d
@@ -0,0 +1,39 @@
d8307d
+/* Test propagation of NODELETE to an already-loaded object via relocation.
d8307d
+   Non-NODELETE helper module.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Globally exported.  Set by the main program to true before
d8307d
+   termination, and used by tst-dlopen-nodelete-reloc-mod2.so to
d8307d
+   trigger marking this module as NODELETE (and also for its destructor
d8307d
+   check).  */
d8307d
+bool may_finalize_mod1 = false;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod1)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod1.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod10.c b/elf/tst-dlopen-nodelete-reloc-mod10.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..30748b73ec7daed3
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod10.c
d8307d
@@ -0,0 +1,41 @@
d8307d
+/* Helper module to load tst-dlopen-nodelete-reloc-mod11.so.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <dlfcn.h>
d8307d
+#include <stddef.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+static void *handle;
d8307d
+
d8307d
+static void __attribute__ ((constructor))
d8307d
+init (void)
d8307d
+{
d8307d
+  handle = dlopen ("tst-dlopen-nodelete-reloc-mod11.so", RTLD_NOW);
d8307d
+  if (handle == NULL)
d8307d
+    {
d8307d
+      printf ("error: dlopen in module 10: %s\n", dlerror ());
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  dlclose (handle);
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod11.cc b/elf/tst-dlopen-nodelete-reloc-mod11.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..48c910403e782c83
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod11.cc
d8307d
@@ -0,0 +1,49 @@
d8307d
+/* Second module defining a unique symbol (loaded indirectly).
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Just a flag here, not used for NODELETE processing.  */
d8307d
+bool may_finalize_mod11 = false;
d8307d
+
d8307d
+/* Trigger the creation of a unique symbol reference.  This should
d8307d
+   cause tst-dlopen-nodelete-reloc-mod9.so to be marked as
d8307d
+   NODELETE.  */
d8307d
+
d8307d
+extern template struct unique_symbol<9>;
d8307d
+
d8307d
+int
d8307d
+global_function_mod11 (void)
d8307d
+{
d8307d
+  return unique_symbol<9>::value;
d8307d
+}
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod11)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod11.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod12.cc b/elf/tst-dlopen-nodelete-reloc-mod12.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..5c093fd02d1fd0c7
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod12.cc
d8307d
@@ -0,0 +1,42 @@
d8307d
+/* First module for NODELETE test defining a unique symbol (with DT_NEEDED).
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Just a flag here, not used for NODELETE processing.  */
d8307d
+bool may_finalize_mod12 = false;
d8307d
+
d8307d
+/* Explicit instantiation.  This produces a unique symbol definition
d8307d
+   which is not referenced by the library itself, so the library is
d8307d
+   not marked NODELETE.  */
d8307d
+template struct unique_symbol<12>;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod12)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod12.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod13.cc b/elf/tst-dlopen-nodelete-reloc-mod13.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..caf4fd1cc9e1c1e1
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod13.cc
d8307d
@@ -0,0 +1,48 @@
d8307d
+/* Second module for NODELETE test defining a unique symbol (with DT_NEEDED).
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Just a flag here, not used for NODELETE processing.  */
d8307d
+bool may_finalize_mod13 = false;
d8307d
+
d8307d
+extern template struct unique_symbol<12>;
d8307d
+
d8307d
+/* Trigger the creation of a unique symbol reference.  This should
d8307d
+   cause tst-dlopen-nodelete-reloc-mod12.so to be marked as
d8307d
+   NODELETE.  */
d8307d
+int
d8307d
+global_function_mod13 (void)
d8307d
+{
d8307d
+  return unique_symbol<12>::value;
d8307d
+}
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod13)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod13.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod13.h b/elf/tst-dlopen-nodelete-reloc-mod13.h
d8307d
new file mode 100644
d8307d
index 0000000000000000..5d338481a34a5714
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod13.h
d8307d
@@ -0,0 +1,24 @@
d8307d
+/* Inline function which produces a unique symbol.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+inline char *
d8307d
+third_function_with_local_static (void)
d8307d
+{
d8307d
+  static char local;
d8307d
+  return &local;
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod14.cc b/elf/tst-dlopen-nodelete-reloc-mod14.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..e67621a2a2f8509a
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod14.cc
d8307d
@@ -0,0 +1,42 @@
d8307d
+/* This object must retain NODELETE status after a dlopen failure.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Just a flag here, not used for NODELETE processing.  */
d8307d
+bool may_finalize_mod14 = false;
d8307d
+
d8307d
+/* Explicit instantiation.  This produces a unique symbol definition
d8307d
+   which is not referenced by the library itself, so the library is
d8307d
+   not marked NODELETE.  */
d8307d
+template struct unique_symbol<14>;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod14)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod14.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod15.cc b/elf/tst-dlopen-nodelete-reloc-mod15.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..ead362bfdbb90eef
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod15.cc
d8307d
@@ -0,0 +1,42 @@
d8307d
+/* Helper object to mark tst-dlopen-nodelete-reloc-mod14.so as NODELETE.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+extern template struct unique_symbol<14>;
d8307d
+
d8307d
+/* Trigger the creation of a unique symbol reference.  This should
d8307d
+   cause tst-dlopen-nodelete-reloc-mod14.so to be marked as
d8307d
+   NODELETE.  */
d8307d
+int
d8307d
+global_function_mod15 (void)
d8307d
+{
d8307d
+  return unique_symbol<14>::value;
d8307d
+}
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  /* This object is never loaded completely.  */
d8307d
+  puts ("error: tst-dlopen-nodelete-reloc-mod15.so destructor invoked");
d8307d
+  _exit (1);
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod16.c b/elf/tst-dlopen-nodelete-reloc-mod16.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..fa2ed1461b42c82c
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod16.c
d8307d
@@ -0,0 +1,27 @@
d8307d
+/* Object with an undefined symbol to trigger a relocation failure.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* The reference to undefined_mod16 triggers a relocation failure.  */
d8307d
+
d8307d
+extern int undefined_mod16;
d8307d
+
d8307d
+int
d8307d
+global_function_mod16 (void)
d8307d
+{
d8307d
+  return undefined_mod16;
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod17.c b/elf/tst-dlopen-nodelete-reloc-mod17.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..426562edd9a3ffee
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod17.c
d8307d
@@ -0,0 +1,19 @@
d8307d
+/* Top-level object with dependency on an object that fails relocation.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* The dependencies do all the work.  */
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod2.c b/elf/tst-dlopen-nodelete-reloc-mod2.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..81ea8e5af2d00b93
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod2.c
d8307d
@@ -0,0 +1,38 @@
d8307d
+/* Test propagation of NODELETE to an already-loaded object via relocation.
d8307d
+   NODELETE helper module.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Defined in tst-dlopen-nodelete-reloc-mod1.so.  This dependency is
d8307d
+   not expressed via DT_NEEDED, so this reference marks the other
d8307d
+   object as NODELETE dynamically, during initially relocation.  */
d8307d
+extern bool may_finalize_mod1;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod1)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod2.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod3.c b/elf/tst-dlopen-nodelete-reloc-mod3.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..d33f4ec7630c6a1e
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod3.c
d8307d
@@ -0,0 +1,38 @@
d8307d
+/* Test propagation of NODELETE to an already-loaded object via relocation.
d8307d
+   Non-NODELETE helper module.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Globally exported.  Set by the main program to true before
d8307d
+   termination, and used by tst-dlopen-nodelete-reloc-mod4.so,
d8307d
+   tst-dlopen-nodelete-reloc-mod5.so.  */
d8307d
+bool may_finalize_mod3 = false;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod3)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod3.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod4.c b/elf/tst-dlopen-nodelete-reloc-mod4.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..7e6633aebb1e2f00
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod4.c
d8307d
@@ -0,0 +1,37 @@
d8307d
+/* Test propagation of NODELETE to an already-loaded object via relocation.
d8307d
+   Intermediate helper module.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Defined in tst-dlopen-nodelete-reloc-mod3.so.  The dependency is
d8307d
+   expressed via DT_NEEDED.  */
d8307d
+extern bool may_finalize_mod3;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod3)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod4.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod5.c b/elf/tst-dlopen-nodelete-reloc-mod5.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..22aa16f855dc75a8
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod5.c
d8307d
@@ -0,0 +1,38 @@
d8307d
+/* Test propagation of NODELETE to an already-loaded object via relocation.
d8307d
+   NODELETE helper module.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Defined in tst-dlopen-nodelete-reloc-mod3.so.  The dependency is
d8307d
+   expressed via DT_NEEDED on the intermediate DSO
d8307d
+   tst-dlopen-nodelete-reloc-mod3.so.  */
d8307d
+extern bool may_finalize_mod3;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod3)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod5.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod6.cc b/elf/tst-dlopen-nodelete-reloc-mod6.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..180f5b5842f1c2b0
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod6.cc
d8307d
@@ -0,0 +1,42 @@
d8307d
+/* First module for NODELETE test defining a unique symbol.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Just a flag here, not used for NODELETE processing.  */
d8307d
+bool may_finalize_mod6 = false;
d8307d
+
d8307d
+/* Explicit instantiation.  This produces a unique symbol definition
d8307d
+   which is not referenced by the library itself, so the library is
d8307d
+   not marked NODELETE.  */
d8307d
+template struct unique_symbol<6>;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod6)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod6.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod7.cc b/elf/tst-dlopen-nodelete-reloc-mod7.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..c85e7c991b098bf5
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod7.cc
d8307d
@@ -0,0 +1,48 @@
d8307d
+/* Second module for NODELETE test defining a unique symbol.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Just a flag here, not used for NODELETE processing.  */
d8307d
+bool may_finalize_mod7 = false;
d8307d
+
d8307d
+extern template struct unique_symbol<6>;
d8307d
+
d8307d
+/* Trigger the creation of a unique symbol reference.  This should
d8307d
+   cause tst-dlopen-nodelete-reloc-mod6.so to be marked as
d8307d
+   NODELETE.  */
d8307d
+int
d8307d
+global_function_mod7 (void)
d8307d
+{
d8307d
+  return unique_symbol<6>::value;
d8307d
+}
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod7)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod7.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod8.c b/elf/tst-dlopen-nodelete-reloc-mod8.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..ebb1c35fab57e319
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod8.c
d8307d
@@ -0,0 +1,41 @@
d8307d
+/* Helper module to load tst-dlopen-nodelete-reloc-mod9.so.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <dlfcn.h>
d8307d
+#include <stddef.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+static void *handle;
d8307d
+
d8307d
+static void __attribute__ ((constructor))
d8307d
+init (void)
d8307d
+{
d8307d
+  handle = dlopen ("tst-dlopen-nodelete-reloc-mod9.so", RTLD_NOW);
d8307d
+  if (handle == NULL)
d8307d
+    {
d8307d
+      printf ("error: dlopen in module 8: %s\n", dlerror ());
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  dlclose (handle);
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc-mod9.cc b/elf/tst-dlopen-nodelete-reloc-mod9.cc
d8307d
new file mode 100644
d8307d
index 0000000000000000..06fb49cdf753cb41
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc-mod9.cc
d8307d
@@ -0,0 +1,42 @@
d8307d
+/* First module defining a unique symbol (loaded indirectly).
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include "tst-dlopen-nodelete-reloc.h"
d8307d
+
d8307d
+#include <stdbool.h>
d8307d
+#include <stdio.h>
d8307d
+#include <unistd.h>
d8307d
+
d8307d
+/* Just a flag here, not used for NODELETE processing.  */
d8307d
+bool may_finalize_mod9 = false;
d8307d
+
d8307d
+/* Explicit instantiation.  This produces a unique symbol definition
d8307d
+   which is not referenced by the library itself, so the library is
d8307d
+   not marked NODELETE.  */
d8307d
+template struct unique_symbol<9>;
d8307d
+
d8307d
+static void __attribute__ ((destructor))
d8307d
+fini (void)
d8307d
+{
d8307d
+  if (!may_finalize_mod9)
d8307d
+    {
d8307d
+      puts ("error: tst-dlopen-nodelete-reloc-mod9.so destructor"
d8307d
+            " called too early");
d8307d
+      _exit (1);
d8307d
+    }
d8307d
+}
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc.c b/elf/tst-dlopen-nodelete-reloc.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..291ac9eb8385a92e
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc.c
d8307d
@@ -0,0 +1,179 @@
d8307d
+/* Test interactions of dlopen, NODELETE, and relocations.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* This test exercises NODELETE propagation due to data relocations
d8307d
+   and unique symbols, and the interaction with already-loaded
d8307d
+   objects.  Some test objects are written in C++, to produce unique
d8307d
+   symbol definitions.
d8307d
+
d8307d
+   First test: Global scope variant, data relocation as the NODELETE
d8307d
+   trigger.  mod1 is loaded first with a separate dlopen call.
d8307d
+
d8307d
+      mod2 ---(may_finalize_mod1 relocation dependency)---> mod1
d8307d
+    (NODELETE)                                   (marked as NODELETE)
d8307d
+
d8307d
+   Second test: Local scope variant, data relocation.  mod3 is loaded
d8307d
+   first, then mod5.
d8307d
+
d8307d
+      mod5 ---(DT_NEEDED)--->  mod4  ---(DT_NEEDED)---> mod3
d8307d
+    (NODELETE)           (not NODELETE)                  ^
d8307d
+        \                                               / (marked as
d8307d
+         `--(may_finalize_mod3 relocation dependency)--/   NODELETE)
d8307d
+
d8307d
+   Third test: Shared local scope with unique symbol.  mod6 is loaded
d8307d
+   first, then mod7.  No explicit dependencies between the two
d8307d
+   objects, so first object has to be opened with RTLD_GLOBAL.
d8307d
+
d8307d
+      mod7 ---(unique symbol)---> mod6
d8307d
+                          (marked as NODELETE)
d8307d
+
d8307d
+   Forth test: Non-shared scopes with unique symbol.  mod8 and mod10
d8307d
+   are loaded from the main program.  mod8 loads mod9 from an ELF
d8307d
+   constructor, mod10 loads mod11.  There are no DT_NEEDED
d8307d
+   dependencies.  mod9 is promoted to the global scope form the main
d8307d
+   program.  The unique symbol dependency is:
d8307d
+
d8307d
+      mod9 ---(unique symbol)---> mod11
d8307d
+                          (marked as NODELETE)
d8307d
+
d8307d
+   Fifth test: Shared local scope with unique symbol, like test 3, but
d8307d
+   this time, there is also a DT_NEEDED dependency (so no RTLD_GLOBAL
d8307d
+   needed):
d8307d
+
d8307d
+                 DT_NEEDED
d8307d
+      mod13 ---(unique symbol)---> mod12
d8307d
+                          (marked as NODELETE)
d8307d
+
d8307d
+   Sixth test: NODELETE status is retained after relocation failure
d8307d
+   with unique symbol dependency.  The object graph ensures that the
d8307d
+   unique symbol binding is processed before the dlopen failure.
d8307d
+
d8307d
+                                        DT_NEEDED
d8307d
+     mod17  --(DT_NEEDED)--> mod15 --(unique symbol)--> mod14
d8307d
+       \                       ^                  (RTLD_NODELETE)
d8307d
+        \                 (DT_NEEDED)
d8307d
+         \                     |
d8307d
+          `---(DT_NEEDED)--> mod16
d8307d
+                       (fails to relocate)
d8307d
+
d8307d
+   mod14 is loaded first, and the loading mod17 is attempted.
d8307d
+   mod14 must remain NODELETE after opening mod17 failed.  */
d8307d
+
d8307d
+#include <stdio.h>
d8307d
+#include <string.h>
d8307d
+#include <stdbool.h>
d8307d
+#include <support/check.h>
d8307d
+#include <support/xdlfcn.h>
d8307d
+
d8307d
+static int
d8307d
+do_test (void)
d8307d
+{
d8307d
+  /* First case: global scope, regular data symbol.  Open the object
d8307d
+     which is not NODELETE initially.  */
d8307d
+  void *mod1 = xdlopen ("tst-dlopen-nodelete-reloc-mod1.so",
d8307d
+                        RTLD_NOW | RTLD_GLOBAL);
d8307d
+  /* This is used to indicate that the ELF destructor may be
d8307d
+     called.  */
d8307d
+  bool *may_finalize_mod1 = xdlsym (mod1, "may_finalize_mod1");
d8307d
+  /* Open the NODELETE object.  */
d8307d
+  void *mod2 = xdlopen ("tst-dlopen-nodelete-reloc-mod2.so", RTLD_NOW);
d8307d
+  /* This has no effect because the DSO is directly marked as
d8307d
+     NODELETE.  */
d8307d
+  xdlclose (mod2);
d8307d
+  /* This has no effect because the DSO has been indirectly marked as
d8307d
+     NODELETE due to a relocation dependency.  */
d8307d
+  xdlclose (mod1);
d8307d
+
d8307d
+  /* Second case: local scope, regular data symbol.  Open the object
d8307d
+     which is not NODELETE initially.  */
d8307d
+  void *mod3 = xdlopen ("tst-dlopen-nodelete-reloc-mod3.so", RTLD_NOW);
d8307d
+  bool *may_finalize_mod3 = xdlsym (mod3, "may_finalize_mod3");
d8307d
+  /* Open the NODELETE object.  */
d8307d
+  void *mod5 = xdlopen ("tst-dlopen-nodelete-reloc-mod5.so", RTLD_NOW);
d8307d
+  /* Again those have no effect because of NODELETE.  */
d8307d
+  xdlclose (mod5);
d8307d
+  xdlclose (mod3);
d8307d
+
d8307d
+  /* Third case: Unique symbol.  */
d8307d
+  void *mod6 = xdlopen ("tst-dlopen-nodelete-reloc-mod6.so",
d8307d
+                        RTLD_NOW | RTLD_GLOBAL);
d8307d
+  bool *may_finalize_mod6 = xdlsym (mod6, "may_finalize_mod6");
d8307d
+  void *mod7 = xdlopen ("tst-dlopen-nodelete-reloc-mod7.so", RTLD_NOW);
d8307d
+  bool *may_finalize_mod7 = xdlsym (mod7, "may_finalize_mod7");
d8307d
+  /* This should not have any effect because of the unique symbol and
d8307d
+     the resulting NODELETE status.  */
d8307d
+  xdlclose (mod6);
d8307d
+  /* mod7 is not NODELETE and can be closed.  */
d8307d
+  *may_finalize_mod7 = true;
d8307d
+  xdlclose (mod7);
d8307d
+
d8307d
+  /* Fourth case: Unique symbol, indirect loading.  */
d8307d
+  void *mod8 = xdlopen ("tst-dlopen-nodelete-reloc-mod8.so", RTLD_NOW);
d8307d
+  /* Also promote to global scope.  */
d8307d
+  void *mod9 = xdlopen ("tst-dlopen-nodelete-reloc-mod9.so",
d8307d
+                        RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);
d8307d
+  bool *may_finalize_mod9 = xdlsym (mod9, "may_finalize_mod9");
d8307d
+  xdlclose (mod9);              /* Drop mod9 reference.  */
d8307d
+  void *mod10 = xdlopen ("tst-dlopen-nodelete-reloc-mod10.so", RTLD_NOW);
d8307d
+  void *mod11 = xdlopen ("tst-dlopen-nodelete-reloc-mod11.so",
d8307d
+                        RTLD_NOW | RTLD_NOLOAD);
d8307d
+  bool *may_finalize_mod11 = xdlsym (mod11, "may_finalize_mod11");
d8307d
+  xdlclose (mod11);              /* Drop mod11 reference.  */
d8307d
+  /* mod11 is not NODELETE and can be closed.  */
d8307d
+  *may_finalize_mod11 = true;
d8307d
+  /* Trigger closing of mod11, too.  */
d8307d
+  xdlclose (mod10);
d8307d
+  /* Does not trigger closing of mod9.  */
d8307d
+  xdlclose (mod8);
d8307d
+
d8307d
+  /* Fifth case: Unique symbol, with DT_NEEDED dependency.  */
d8307d
+  void *mod12 = xdlopen ("tst-dlopen-nodelete-reloc-mod12.so", RTLD_NOW);
d8307d
+  bool *may_finalize_mod12 = xdlsym (mod12, "may_finalize_mod12");
d8307d
+  void *mod13 = xdlopen ("tst-dlopen-nodelete-reloc-mod13.so", RTLD_NOW);
d8307d
+  bool *may_finalize_mod13 = xdlsym (mod13, "may_finalize_mod13");
d8307d
+  /* This should not have any effect because of the unique symbol. */
d8307d
+  xdlclose (mod12);
d8307d
+  /* mod13 is not NODELETE and can be closed.  */
d8307d
+  *may_finalize_mod13 = true;
d8307d
+  xdlclose (mod13);
d8307d
+
d8307d
+  /* Sixth case: Unique symbol binding must not cause loss of NODELETE
d8307d
+     status.  */
d8307d
+  void *mod14 = xdlopen ("tst-dlopen-nodelete-reloc-mod14.so",
d8307d
+                         RTLD_NOW | RTLD_NODELETE);
d8307d
+  bool *may_finalize_mod14 = xdlsym (mod14, "may_finalize_mod14");
d8307d
+  TEST_VERIFY (dlopen ("tst-dlopen-nodelete-reloc-mod17.so", RTLD_NOW)
d8307d
+               == NULL);
d8307d
+  const char *message = dlerror ();
d8307d
+  printf ("info: test 6 message: %s\n", message);
d8307d
+  /* This must not close the object, it must still be NODELETE.  */
d8307d
+  xdlclose (mod14);
d8307d
+  xdlopen ("tst-dlopen-nodelete-reloc-mod14.so", RTLD_NOW | RTLD_NOLOAD);
d8307d
+
d8307d
+  /* Prepare for process exit.  Destructors for NODELETE objects will
d8307d
+     be invoked.  */
d8307d
+  *may_finalize_mod1 = true;
d8307d
+  *may_finalize_mod3 = true;
d8307d
+  *may_finalize_mod6 = true;
d8307d
+  *may_finalize_mod9 = true;
d8307d
+  *may_finalize_mod12 = true;
d8307d
+  *may_finalize_mod14 = true;
d8307d
+  return 0;
d8307d
+}
d8307d
+
d8307d
+#include <support/test-driver.c>
d8307d
diff --git a/elf/tst-dlopen-nodelete-reloc.h b/elf/tst-dlopen-nodelete-reloc.h
d8307d
new file mode 100644
d8307d
index 0000000000000000..8844de622631f575
d8307d
--- /dev/null
d8307d
+++ b/elf/tst-dlopen-nodelete-reloc.h
d8307d
@@ -0,0 +1,35 @@
d8307d
+/* Template to produce unique symbols.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <https://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+/* This template produces a unique symbol definition for an explicit
d8307d
+   template instantiation (without also incorporating a reference),
d8307d
+   and an extern template declaration can be used to reference that
d8307d
+   symbol from another object.  The modid parameter is just a
d8307d
+   placeholder to create different symbols (because it affects the
d8307d
+   name mangling of the static value member).  By convention, it
d8307d
+   should match the number of the module that contains the
d8307d
+   definition.  */
d8307d
+
d8307d
+template <int modid>
d8307d
+struct unique_symbol
d8307d
+{
d8307d
+  static int value;
d8307d
+};
d8307d
+
d8307d
+template <int modid>
d8307d
+int unique_symbol<modid>::value;