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