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