unofficial mirror of libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen
@ 2021-07-08 16:32 Vivek Das Mohapatra via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 1/8] Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745) Vivek Das Mohapatra via Libc-alpha
                   ` (9 more replies)
  0 siblings, 10 replies; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

This is a revision of a previous patchset that I posted here
regarding https://sourceware.org/bugzilla/show_bug.cgi?id=22745 

Introduction:

=======================================================================
  As discussed in the URL above dlmopen requires a mechanism for
  [optionally] sharing some objects between more than one namespace.

  The following patchset provides an implementation for this: If an
  object is loaded with the new RTLD_SHARED flag we instead ensure
  that a "master" copy exists (and is flagged as no-delete) in the
  main namespace and a thin wrapper or clone is placed in the target
  namespace.

  This patch series should address all the comments received on the
  earlier (v1-v10) series.

=======================================================================

Changes from v11:

 - If a DSO is required in a non-base namespace because it is mentioned
   in a DT_NEEDED entry and it is itself flagged DF_GNU_1_UNIQUE then
   a proxy is generated for it.

 - Relocations via non-base namespace proxies work reliably (some code
   paths did not do the address calculation relative to the DSO base
   correctly when a proxy was involved).

 - Tests extended cover the above two scenarios.

Changes from v10:

 - A segfault in a dlmopen error pathway (which does not seem to have existed
   when v10 was applied to the then-HEAD commit) has been fixed.

 - The fallback mechanism for adding DT_GNU_FLAGS_1 sections to the required
   binaries has had some infrastructure moved to the elf/ directory

 - The runstatedir setting introduced by recent autoconf has been omitted from
   the patchset as it is not relevant to this feature.

 - libpthread no longer tagged DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE.
   (with both -z unique supporting linkers and if the .os hack is
   used to add the new flag - this was inconsistent before).

Not changed:

 - There is still some diagnostic info in the config.log when the linker
   layout is acceptable but -z unique is not yet supported. I believe this
   _is_ useful diagnostic information as a developer might otherwise wonder
   why the vanilla linker was being rejected when its layout output seemed
   fine.

I have not yet implemented, but plan to address once this series is
accepted/acceptable:

 - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)

 - Check RTLD_GLOBAL interacts properly and unsurprisingly with RTLD_SHARED.

Vivek Das Mohapatra (8):
  Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745)
  Abstract loaded-DSO search code into a helper function
  Use the new DSO finder helper function
  Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745)
  Implement dlmopen RTLD_SHARED flag (bug 22745)
  Add dlmopen / RTLD_SHARED tests
  Restore separate libc loading for the TLS/namespace storage test
  Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO

 Makeconfig                           |   3 +
 Makerules                            |  18 +-
 bits/dlfcn.h                         |  10 +
 config.make.in                       |   1 +
 configure                            |  42 +-
 configure.ac                         |  31 +-
 elf/Makefile                         | 109 +++-
 elf/dl-close.c                       |  43 +-
 elf/dl-deps.c                        |  17 +
 elf/dl-fini.c                        |   6 +-
 elf/dl-init.c                        |   4 +-
 elf/dl-load.c                        | 223 ++++++-
 elf/dl-lookup.c                      |  26 +-
 elf/dl-object.c                      |  78 +++
 elf/dl-open.c                        | 121 +++-
 elf/dl-sym.c                         |  14 +
 elf/dynamic-notes.c                  |   4 +
 elf/elf.h                            |   7 +-
 elf/get-dynamic-info.h               |  12 +
 elf/rtld.c                           |   2 +-
 elf/tst-dlmopen-auditmod.c           |  23 +
 elf/tst-dlmopen-common.h             |  33 +
 elf/tst-dlmopen-main.h               | 879 +++++++++++++++++++++++++++
 elf/tst-dlmopen-modules.h            |  21 +
 elf/tst-dlmopen-rtld-audit-shared1.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared2.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared3.c |   7 +
 elf/tst-dlmopen-rtld-audit-shared4.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared5.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared6.c |   8 +
 elf/tst-dlmopen-rtld-audit-unique1.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique2.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique3.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique4.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique5.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique6.c |   7 +
 elf/tst-dlmopen-rtld-shared1.c       |   7 +
 elf/tst-dlmopen-rtld-shared1.h       |  64 ++
 elf/tst-dlmopen-rtld-shared2.c       |   7 +
 elf/tst-dlmopen-rtld-shared2.h       |  66 ++
 elf/tst-dlmopen-rtld-shared3.c       |   7 +
 elf/tst-dlmopen-rtld-shared3.h       |  43 ++
 elf/tst-dlmopen-rtld-shared4.c       |   7 +
 elf/tst-dlmopen-rtld-shared4.h       |  14 +
 elf/tst-dlmopen-rtld-shared5.c       |   7 +
 elf/tst-dlmopen-rtld-shared5.h       |  25 +
 elf/tst-dlmopen-rtld-shared6.c       |   7 +
 elf/tst-dlmopen-rtld-shared6.h       |  36 ++
 elf/tst-dlmopen-rtld-unique1.c       |   7 +
 elf/tst-dlmopen-rtld-unique1.h       |  86 +++
 elf/tst-dlmopen-rtld-unique2.c       |   7 +
 elf/tst-dlmopen-rtld-unique2.h       |  25 +
 elf/tst-dlmopen-rtld-unique3.c       |   7 +
 elf/tst-dlmopen-rtld-unique3.h       |  13 +
 elf/tst-dlmopen-rtld-unique4.c       |   7 +
 elf/tst-dlmopen-rtld-unique4.h       |  14 +
 elf/tst-dlmopen-rtld-unique5.c       |   7 +
 elf/tst-dlmopen-rtld-unique5.h       |  58 ++
 elf/tst-dlmopen-rtld-unique6.c       |   7 +
 elf/tst-dlmopen-rtld-unique6.h       |  51 ++
 elf/tst-dlmopen-sharedmod-norm.c     |  34 ++
 elf/tst-dlmopen-sharedmod-uniq.c     |  33 +
 elf/tst-dlmopen-std-do-test.c        |  12 +
 elf/tst-tls-ie-dlmopen.c             |   4 +-
 extra-lib.mk                         |   5 +
 htl/Makefile                         |   4 +
 iconvdata/Makefile                   |   3 +
 iconvdata/extra-module.mk            |   4 +
 include/elf.h                        |   2 +
 include/link.h                       |   7 +-
 nptl/Makefile                        |   8 +-
 sysdeps/generic/ldsodefs.h           |  11 +
 sysdeps/mips/bits/dlfcn.h            |  10 +
 73 files changed, 2455 insertions(+), 77 deletions(-)
 create mode 100644 elf/dynamic-notes.c
 create mode 100644 elf/tst-dlmopen-auditmod.c
 create mode 100644 elf/tst-dlmopen-common.h
 create mode 100644 elf/tst-dlmopen-main.h
 create mode 100644 elf/tst-dlmopen-modules.h
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared1.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared2.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared3.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared4.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared5.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared6.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique1.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique2.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique3.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique4.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique5.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique6.c
 create mode 100644 elf/tst-dlmopen-rtld-shared1.c
 create mode 100644 elf/tst-dlmopen-rtld-shared1.h
 create mode 100644 elf/tst-dlmopen-rtld-shared2.c
 create mode 100644 elf/tst-dlmopen-rtld-shared2.h
 create mode 100644 elf/tst-dlmopen-rtld-shared3.c
 create mode 100644 elf/tst-dlmopen-rtld-shared3.h
 create mode 100644 elf/tst-dlmopen-rtld-shared4.c
 create mode 100644 elf/tst-dlmopen-rtld-shared4.h
 create mode 100644 elf/tst-dlmopen-rtld-shared5.c
 create mode 100644 elf/tst-dlmopen-rtld-shared5.h
 create mode 100644 elf/tst-dlmopen-rtld-shared6.c
 create mode 100644 elf/tst-dlmopen-rtld-shared6.h
 create mode 100644 elf/tst-dlmopen-rtld-unique1.c
 create mode 100644 elf/tst-dlmopen-rtld-unique1.h
 create mode 100644 elf/tst-dlmopen-rtld-unique2.c
 create mode 100644 elf/tst-dlmopen-rtld-unique2.h
 create mode 100644 elf/tst-dlmopen-rtld-unique3.c
 create mode 100644 elf/tst-dlmopen-rtld-unique3.h
 create mode 100644 elf/tst-dlmopen-rtld-unique4.c
 create mode 100644 elf/tst-dlmopen-rtld-unique4.h
 create mode 100644 elf/tst-dlmopen-rtld-unique5.c
 create mode 100644 elf/tst-dlmopen-rtld-unique5.h
 create mode 100644 elf/tst-dlmopen-rtld-unique6.c
 create mode 100644 elf/tst-dlmopen-rtld-unique6.h
 create mode 100644 elf/tst-dlmopen-sharedmod-norm.c
 create mode 100644 elf/tst-dlmopen-sharedmod-uniq.c
 create mode 100644 elf/tst-dlmopen-std-do-test.c

-- 
2.20.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 1/8] Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745)
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 12:27   ` Adhemerval Zanella via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 2/8] Abstract loaded-DSO search code into a helper function Vivek Das Mohapatra via Libc-alpha
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

Define a new flags section DT_GNU_FLAGS_1 (no more bits are available
in DT_GNU_FLAGS).

One flag is currently defined: DF_GNU_1_UNIQUE.

libc and its companion DSOs (libpthread et al) should have this
section and flag set.
---
 elf/elf.h              |  7 ++++++-
 elf/get-dynamic-info.h | 12 ++++++++++++
 include/elf.h          |  2 ++
 include/link.h         |  1 +
 4 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/elf/elf.h b/elf/elf.h
index 2a62b98d4a..448c275d10 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -896,6 +896,7 @@ typedef struct
    Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's
    approach.  */
 #define DT_VALRNGLO	0x6ffffd00
+#define DT_GNU_FLAGS_1   0x6ffffdf4	/* GNU extension flags */
 #define DT_GNU_PRELINKED 0x6ffffdf5	/* Prelinking timestamp */
 #define DT_GNU_CONFLICTSZ 0x6ffffdf6	/* Size of conflict section */
 #define DT_GNU_LIBLISTSZ 0x6ffffdf7	/* Size of library list */
@@ -910,7 +911,7 @@ typedef struct
 #define DT_SYMINENT	0x6ffffdff	/* Entry size of syminfo */
 #define DT_VALRNGHI	0x6ffffdff
 #define DT_VALTAGIDX(tag)	(DT_VALRNGHI - (tag))	/* Reverse order! */
-#define DT_VALNUM 12
+#define DT_VALNUM 13
 
 /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
    Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
@@ -999,6 +1000,10 @@ typedef struct
 #define	DF_1_WEAKFILTER 0x20000000
 #define	DF_1_NOCOMMON   0x40000000
 
+/* State flags selectable in the `d_un.d_val' element of the DT_GNU_FLAGS_1
+   entry in the dynamic section.  */
+#define DF_GNU_1_UNIQUE 0x00000001	/* Load max 1 copy of this DSO.  */
+
 /* Flags for the feature selection in DT_FEATURE_1.  */
 #define DTF_1_PARINIT	0x00000001
 #define DTF_1_CONFEXP	0x00000002
diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index d8ec32377d..8082d84ba1 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -175,6 +175,18 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
       if (l->l_flags_1 & DF_1_NOW)
 	info[DT_BIND_NOW] = info[VERSYMIDX (DT_FLAGS_1)];
     }
+  if (info[DT_VALTAGIDX (DT_GNU_FLAGS_1)
+           + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM] != NULL)
+    {
+      l->l_gnu_flags_1 = info[DT_VALTAGIDX (DT_GNU_FLAGS_1)
+                              + DT_NUM + DT_THISPROCNUM
+                              + DT_VERSIONTAGNUM + DT_EXTRANUM]->d_un.d_val;
+
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)
+	  && l->l_gnu_flags_1 & ~DT_GNU_1_SUPPORTED_MASK)
+	_dl_debug_printf ("\nWARNING: Unsupported flag value(s) of 0x%x in DT_GNU_FLAGS_1.\n",
+			  l->l_gnu_flags_1 & ~DT_GNU_1_SUPPORTED_MASK);
+    }
   if (info[DT_RUNPATH] != NULL)
     /* If both RUNPATH and RPATH are given, the latter is ignored.  */
     info[DT_RPATH] = NULL;
diff --git a/include/elf.h b/include/elf.h
index 14ed67ff67..5eee37c294 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -25,5 +25,7 @@
    (DF_1_NOW | DF_1_NODELETE | DF_1_INITFIRST | DF_1_NOOPEN \
     | DF_1_ORIGIN | DF_1_NODEFLIB | DF_1_PIE)
 
+#define DT_GNU_1_SUPPORTED_MASK DF_GNU_1_UNIQUE
+
 #endif /* !_ISOMAC */
 #endif /* elf.h */
diff --git a/include/link.h b/include/link.h
index 4af16cb596..6ed35eb808 100644
--- a/include/link.h
+++ b/include/link.h
@@ -283,6 +283,7 @@ struct link_map
     unsigned int l_used;
 
     /* Various flag words.  */
+    ElfW(Word) l_gnu_flags_1;
     ElfW(Word) l_feature_1;
     ElfW(Word) l_flags_1;
     ElfW(Word) l_flags;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 2/8] Abstract loaded-DSO search code into a helper function
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 1/8] Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745) Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 13:04   ` Adhemerval Zanella via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 3/8] Use the new DSO finder " Vivek Das Mohapatra via Libc-alpha
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

---
 elf/dl-load.c              | 38 ++++++++++++++++++++++++++++++++++++++
 sysdeps/generic/ldsodefs.h |  4 ++++
 2 files changed, 42 insertions(+)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index a08df001af..26680b7f68 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2027,6 +2027,44 @@ open_path (const char *name, size_t namelen, int mode,
   return -1;
 }
 
+/* Search for a shared object in a given namespace.  */
+struct link_map *
+_dl_find_dso (const char *name, Lmid_t nsid)
+{
+  struct link_map *l;
+
+  for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
+    {
+      /* If the requested name matches the soname of a loaded object,
+	 use that object.  Elide this check for names that have not
+	 yet been opened.  */
+      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0))
+	continue;
+      if (!_dl_name_match_p (name, l))
+	{
+	  const char *soname;
+
+	  if (__glibc_likely (l->l_soname_added)
+	      || l->l_info[DT_SONAME] == NULL)
+	    continue;
+
+	  soname = ((const char *) D_PTR (l, l_info[DT_STRTAB])
+		    + l->l_info[DT_SONAME]->d_un.d_val);
+	  if (strcmp (name, soname) != 0)
+	    continue;
+
+	  /* We have a match on a new name -- cache it.  */
+	  add_name_to_object (l, soname);
+	  l->l_soname_added = 1;
+	}
+
+      /* We have a match.  */
+      return l;
+    }
+
+  return NULL;
+}
+
 /* Map in the shared object file NAME.  */
 
 struct link_map *
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 176394de4d..44e7097712 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1287,6 +1287,10 @@ extern void _dl_show_scope (struct link_map *new, int from)
 extern struct link_map *_dl_find_dso_for_object (const ElfW(Addr) addr);
 rtld_hidden_proto (_dl_find_dso_for_object)
 
+extern struct link_map *_dl_find_dso (const char *name, Lmid_t nsid);
+rtld_hidden_proto (_dl_find_dso)
+
+
 /* Initialization which is normally done by the dynamic linker.  */
 extern void _dl_non_dynamic_init (void)
      attribute_hidden;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 3/8] Use the new DSO finder helper function
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 1/8] Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745) Vivek Das Mohapatra via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 2/8] Abstract loaded-DSO search code into a helper function Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 12:59   ` Adhemerval Zanella via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745) Vivek Das Mohapatra via Libc-alpha
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

---
 elf/dl-load.c | 28 +++-------------------------
 1 file changed, 3 insertions(+), 25 deletions(-)

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 26680b7f68..050c64135a 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -2082,32 +2082,10 @@ _dl_map_object (struct link_map *loader, const char *name,
   assert (nsid < GL(dl_nns));
 
   /* Look for this name among those already loaded.  */
-  for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
+  l = _dl_find_dso (name, nsid);
+
+  if (l != NULL)
     {
-      /* If the requested name matches the soname of a loaded object,
-	 use that object.  Elide this check for names that have not
-	 yet been opened.  */
-      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0))
-	continue;
-      if (!_dl_name_match_p (name, l))
-	{
-	  const char *soname;
-
-	  if (__glibc_likely (l->l_soname_added)
-	      || l->l_info[DT_SONAME] == NULL)
-	    continue;
-
-	  soname = ((const char *) D_PTR (l, l_info[DT_STRTAB])
-		    + l->l_info[DT_SONAME]->d_un.d_val);
-	  if (strcmp (name, soname) != 0)
-	    continue;
-
-	  /* We have a match on a new name -- cache it.  */
-	  add_name_to_object (l, soname);
-	  l->l_soname_added = 1;
-	}
-
-      /* We have a match.  */
       return l;
     }
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745)
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
                   ` (2 preceding siblings ...)
  2021-07-08 16:32 ` [RFC][PATCH v12 3/8] Use the new DSO finder " Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 16:48   ` Adhemerval Zanella via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag " Vivek Das Mohapatra via Libc-alpha
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

libc.so, libpthread.so etc should have the new unique-dso-by-default
flag set to allow dlmopen to work better (libc et al instance shared
by default when DSOs dlmopened into a new namespace).
---
 Makeconfig                |  3 +++
 Makerules                 | 18 ++++++++++++++++-
 config.make.in            |  1 +
 configure                 | 42 ++++++++++++++++++++++++++++++++++++---
 configure.ac              | 31 ++++++++++++++++++++++++++---
 elf/Makefile              |  3 +++
 elf/dynamic-notes.c       |  4 ++++
 extra-lib.mk              |  3 +++
 htl/Makefile              |  3 +++
 iconvdata/Makefile        |  3 +++
 iconvdata/extra-module.mk |  4 ++++
 nptl/Makefile             |  7 ++++++-
 12 files changed, 114 insertions(+), 8 deletions(-)
 create mode 100644 elf/dynamic-notes.c

diff --git a/Makeconfig b/Makeconfig
index efc7351d71..baa893840e 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -398,6 +398,9 @@ LDFLAGS-lib.so += -Wl,-z,now
 # Extra flags for dynamically linked non-test main programs.
 link-extra-flags += -Wl,-z,now
 endif
+ifeq ($(ld-zunique),yes)
+LDFLAGS-lib.so += -Wl,-z,unique
+endif
 
 # Command to run after every final link (executable or shared object).
 # This is invoked with $(call after-link,...), so it should operate on
diff --git a/Makerules b/Makerules
index 596fa68376..66c1251754 100644
--- a/Makerules
+++ b/Makerules
@@ -581,7 +581,8 @@ $(common-objpfx)shlib.lds: $(common-objpfx)config.make $(..)Makerules
 		 PROVIDE(__start___libc_IO_vtables = .);\
 		 __libc_IO_vtables : { *(__libc_IO_vtables) }\
 		 PROVIDE(__stop___libc_IO_vtables = .);\
-		 /DISCARD/ : { *(.gnu.glibc-stub.*) }@'
+		 /DISCARD/ : { *(.gnu.glibc-stub.*) }@' \
+	      -e 's/^.*\*(\.dynamic).*$$/   .dynamic : { *dynamic-notes.os(.dynamic) *(.dynamic) }/'
 	test -s $@T
 	mv -f $@T $@
 common-generated += shlib.lds
@@ -636,6 +637,16 @@ build-shlib-objlist = $(build-module-helper-objlist) \
 # Also omits crti.o and crtn.o, which we do not want
 # since we define our own `.init' section specially.
 LDFLAGS-c.so = -nostdlib -nostartfiles
+
+# The stub dynamic-notes.os should not have weak/undefined symbol magic in it.
+# It's not really part of the internals, rather it is a vector for linker magic
+# which we need when the linker isn't new enough:
+%/dynamic-notes.os: CPPFLAGS += -UMODULE_NAME
+
+ifeq ($(ld-zunique),yes)
+LDFLAGS-c.so += -Wl,-z,unique
+endif
+
 # But we still want to link libc.so against $(libc.so-gnulib).
 LDLIBS-c.so += $(libc.so-gnulib)
 # Give libc.so an entry point and make it directly runnable itself.
@@ -706,6 +717,11 @@ $(common-objpfx)linkobj/libc.so: $(common-objpfx)linkobj/libc_pic.a \
 	$(build-shlib)
 	$(call after-link,$@)
 
+ifneq ($(ld-zunique),yes)
+$(common-objpfx)libc.so: $(common-objpfx)elf/dynamic-notes.os
+$(common-objpfx)linkobj/libc.so: $(common-objpfx)elf/dynamic-notes.os
+endif
+
 ifeq ($(build-shared),yes)
 $(common-objpfx)libc.so: $(common-objpfx)libc.map
 endif
diff --git a/config.make.in b/config.make.in
index cbf59114b0..8755490c13 100644
--- a/config.make.in
+++ b/config.make.in
@@ -72,6 +72,7 @@ have-cc-with-libunwind = @libc_cv_cc_with_libunwind@
 fno-unit-at-a-time = @fno_unit_at_a_time@
 bind-now = @bindnow@
 have-hash-style = @libc_cv_hashstyle@
+ld-zunique = @ld_zunique@
 use-default-link = @use_default_link@
 have-cxx-thread_local = @libc_cv_cxx_thread_local@
 have-loop-to-function = @libc_cv_cc_loop_to_function@
diff --git a/configure b/configure
index 9619c10991..a53243a178 100755
--- a/configure
+++ b/configure
@@ -625,6 +625,7 @@ libc_cv_cc_nofma
 libc_cv_mtls_dialect_gnu2
 fno_unit_at_a_time
 libc_cv_has_glob_dat
+ld_zunique
 libc_cv_hashstyle
 libc_cv_fpie
 libc_cv_z_execstack
@@ -6074,9 +6075,38 @@ fi
 $as_echo "$libc_cv_hashstyle" >&6; }
 
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support" >&5
+$as_echo_n "checking for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support... " >&6; }
+if ${libc_cv_ld_zunique+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat > conftest.ld.c <<\EOF
+int x (void) { return 0; }
+EOF
+
+libc_cv_ld_zunique=no;
+
+if { ac_try='${CC-cc} -Wl,--fatal-warnings -Wl,-z,unique -shared -o conftest.ld.so conftest.ld.c'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+then
+  libc_cv_ld_zunique=yes
+fi;
+ld_zunique=$libc_cv_ld_zunique
+rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_ld_zunique" >&5
+$as_echo "$libc_cv_ld_zunique" >&6; }
+
+
 # The linker's default -shared behavior is good enough if it
 # does these things that our custom linker scripts ensure that
-# all allocated NOTE sections come first.
+# all allocated NOTE sections come first AND it understands
+# -z unique should result in DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE
+# sections in its output.
 if test "$use_default_link" = default; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sufficient default -shared layout" >&5
 $as_echo_n "checking for sufficient default -shared layout... " >&6; }
@@ -6119,10 +6149,16 @@ EOF
       esac
       shift 2
     done
-    case "$libc_seen_a$libc_seen_b" in
-    yesyes)
+    case "$libc_seen_a$libc_seen_b$libc_cv_ld_zunique" in
+    yesyesyes)
       libc_cv_use_default_link=yes
       ;;
+    yesyesno)
+      echo >&5 "\
+shared layout from:
+$ac_try
+is OK but -Wl,-z,unique is unsupported so linker scripts are required"
+      ;;
     *)
       echo >&5 "\
 $libc_seen_a$libc_seen_b from:
diff --git a/configure.ac b/configure.ac
index 34ecbba540..9369bcbebe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1372,9 +1372,28 @@ fi
 rm -f conftest*])
 AC_SUBST(libc_cv_hashstyle)
 
+AC_CACHE_CHECK([for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support],
+                libc_cv_ld_zunique, [dnl
+cat > conftest.ld.c <<\EOF
+int x (void) { return 0; }
+EOF
+
+libc_cv_ld_zunique=no;
+
+if AC_TRY_COMMAND([dnl
+${CC-cc} -Wl,--fatal-warnings -Wl,-z,unique -shared -o conftest.ld.so conftest.ld.c])
+then
+  libc_cv_ld_zunique=yes
+fi;
+ld_zunique=$libc_cv_ld_zunique
+rm -f conftest*])
+AC_SUBST(ld_zunique)
+
 # The linker's default -shared behavior is good enough if it
 # does these things that our custom linker scripts ensure that
-# all allocated NOTE sections come first.
+# all allocated NOTE sections come first AND it understands
+# -z unique should result in DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE
+# sections in its output.
 if test "$use_default_link" = default; then
   AC_CACHE_CHECK([for sufficient default -shared layout],
 		  libc_cv_use_default_link, [dnl
@@ -1410,10 +1429,16 @@ EOF
       esac
       shift 2
     done
-    case "$libc_seen_a$libc_seen_b" in
-    yesyes)
+    case "$libc_seen_a$libc_seen_b$libc_cv_ld_zunique" in
+    yesyesyes)
       libc_cv_use_default_link=yes
       ;;
+    yesyesno)
+      echo >&AS_MESSAGE_LOG_FD "\
+shared layout from:
+$ac_try
+is OK but -Wl,-z,unique is unsupported so linker scripts are required"
+      ;;
     *)
       echo >&AS_MESSAGE_LOG_FD "\
 $libc_seen_a$libc_seen_b from:
diff --git a/elf/Makefile b/elf/Makefile
index 698a6ab985..9734030c23 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -103,6 +103,9 @@ endif
 
 ifeq (yes,$(build-shared))
 extra-objs	= $(all-rtld-routines:%=%.os) sofini.os interp.os
+ifneq (yes,$(ld-zunique))
+extra-objs      += dynamic-notes.os
+endif
 generated	+= librtld.os dl-allobjs.os ld.so ldd
 install-others	= $(inst_rtlddir)/$(rtld-installed-name)
 install-bin-script = ldd
diff --git a/elf/dynamic-notes.c b/elf/dynamic-notes.c
new file mode 100644
index 0000000000..d0b487e970
--- /dev/null
+++ b/elf/dynamic-notes.c
@@ -0,0 +1,4 @@
+#include <link.h>
+
+const ElfW(Dyn) __dynamic_note __attribute__ ((section (".dynamic"))) =
+  { .d_tag = DT_GNU_FLAGS_1, .d_un.d_val = DF_GNU_1_UNIQUE };
diff --git a/extra-lib.mk b/extra-lib.mk
index 72f8d2e1df..9051958ec0 100644
--- a/extra-lib.mk
+++ b/extra-lib.mk
@@ -101,6 +101,9 @@ $(objpfx)$(lib).so: $(firstword $($(lib)-map) \
 				$(addprefix $(common-objpfx), \
 					    $(filter $(lib).map, \
 						     $(version-maps))))
+ifneq ($(ld-zunique),yes)
+$(objpfx)$(lib).so: $(common-objpfx)/elf/dynamic-notes.os
+endif
 endif
 
 endif
diff --git a/htl/Makefile b/htl/Makefile
index cf9d12fc12..c2a25dcd79 100644
--- a/htl/Makefile
+++ b/htl/Makefile
@@ -203,6 +203,9 @@ libc-link.so = $(common-objpfx)libc.so
 
 extra-B-pthread.so = -B$(common-objpfx)htl/
 LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
+ifeq ($(ld-zunique),yes)
+LDFLAGS-pthread.so += -Wl,-z,unique
+endif
 
 include ../Rules
 
diff --git a/iconvdata/Makefile b/iconvdata/Makefile
index bb3f621b49..4170005322 100644
--- a/iconvdata/Makefile
+++ b/iconvdata/Makefile
@@ -67,6 +67,9 @@ modules	:= ISO8859-1 ISO8859-2 ISO8859-3 ISO8859-4 ISO8859-5		 \
 ifeq ($(bind-now),yes)
 LDFLAGS.so += -Wl,-z,now
 endif
+ifeq ($(ld-zunique),yes)
+LDFLAGS.so += -Wl,-z,unique
+endif
 
 modules.so := $(addsuffix .so, $(modules))
 
diff --git a/iconvdata/extra-module.mk b/iconvdata/extra-module.mk
index ecaf507624..92c3ab4d7b 100644
--- a/iconvdata/extra-module.mk
+++ b/iconvdata/extra-module.mk
@@ -7,6 +7,10 @@ $(objpfx)$(mod).so: $(addprefix $(objpfx),$(addsuffix .os,$($(mod)-routines)))\
 		    $(shlib-lds) $(link-libc-deps)
 	$(build-module-asneeded)
 
+ifneq ($(ld-zunique),yes)
+$(objpfx)$(mod).so: $(common-objpfx)/elf/dynamic-notes.os
+endif
+
 ifneq (,$(extra-modules-left))
 include extra-module.mk
 endif
diff --git a/nptl/Makefile b/nptl/Makefile
index 9b94bfcd31..81353b9455 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -375,7 +375,12 @@ CPPFLAGS-tst-pthread-gdb-attach-static.c := \
 # were launched with an explicit ld.so invocation.
 tst-pthread-gdb-attach-no-pie = yes
 
-tests += tst-cancelx7 tst-cancelx17
+LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
+ifeq ($(ld-zunique),yes)
+LDFLAGS-pthread.so += -Wl,-z,unique
+endif
+
+tests += tst-cancelx7 tst-cancelx17 tst-cleanupx4
 
 ifeq ($(build-shared),yes)
 tests += tst-compat-forwarder tst-audit-threads
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag (bug 22745)
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
                   ` (3 preceding siblings ...)
  2021-07-08 16:32 ` [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745) Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 19:07   ` Adhemerval Zanella via Libc-alpha
  2021-08-09 20:30   ` Adhemerval Zanella via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 6/8] Add dlmopen / RTLD_SHARED tests Vivek Das Mohapatra via Libc-alpha
                   ` (4 subsequent siblings)
  9 siblings, 2 replies; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

This flag will instruct dlmopen to create a shared object present
in the main namespace and accessible from the selected namespace
when supplied in the MODE argument.

include/link.h: Update the link_map struct to allow proxies

We already have an l_real pointer, used for a similar purpose by
the linker for copies of ld.so in secondary namespaces. Update its
documentation and add a bitfield to indicate when link_map entry
is a proxy.

elf/dl-object.c: Implement a helper function to proxy link_map entries

Provides the minimal functionality needed to take an existing
link_map entry and create a proxy for it in the specified namespace.

elf/dl-load.c, elf/dl-open.c: Implement RTLD_SHARED dlmopen proxying

This uses the new infrastructure to implement RTLD_SHARED object
proxying via dlmopen: Instead of opening the specified object in
the requested namespace we open it in the main namespace (if it
is not already present there) and proxy it to the destination.

The following rules apply:

If a proxy of the object is already present in the requested namespace,
we simply return it (with an incremented direct-open count).

If the object is already present in the requested namespace, a dl
error is signalled, since we cannot satisfy the user's request.

Proxies are never created in the main namespace: RTLD_SHARED has no
effect when the requested namespace is LM_ID_BASE.

elf/dl-fini.c: Handle proxy link_maps in the shutdown path (bug 22745)

When cleaning up before exit we should not call destructors or
otherwise free [most of] the contents of proxied link_map entries
since they share [most of] their contents with the LM_ID_BASE
objects to which they point.

elf/dl-init.c: Skip proxy link_map entries in dl init path

Proxies should not trigger calls to DT_INIT constructors since they're
just shims that point to the real, already loaded and initialised, objects.

elf/dl-open.c: Skip libc init if namespace has no libc map

Secondary namespaces which share their libc mapping with the main
namespace cannot (and should not) have _dl_call_libc_early_init
called for them by dl_open_worker.

elf/dl-open.c: When creating a proxy check NS 0 libc map

The libc_already_loaded check normally considers the libc_map entry
in GL(dl_ns)[args->nsid].libc_map.

This is not correct for proxies, which use the libc_map from
the default namespace (as proxies are dummy entries that point
to the base namespace via their l_real members).

elf/dl-load.c, dl-open.c: Compare DSOs by file ID & check DF_GNU_1_UNIQUE

If _dl_map_object_from_fd finds that a DSO it was asked to
load into a non-base namespace is already loaded (into the
main namespace) and is flagged DF_GNU_1_UNIQUE then it should
return that DSO's link map entry.

In such cases _dl_open_worker must notice that this has
happened and continue down the link map proxy generation
path instead of normal link map entry preparation.

elf/dl-load.c: error if RTLD_SHARED = DF_GNU_1_UNIQUE semantics violated

elf/dl-open.c: Use search helper to find preloaded DT_GNU_UNIQUE DSOs

If a DSO already exists (with the same name) in the base namespace
and it is flagged DT_GNU_UNIQUE then we should behave as if a proxy
had been requested.

elf/dl-load.c: When loading DSOs in other namespaces check DT_GNU_UNIQUE

If a DSO has not already been loaded and the target is not the main
namespace then we must check to see if it's been DT_GNU_UNIQUE tagged
and load it into the main namespace instead.

dl_open_worker has alread been modified to notice the discrepancy
between the request and the result in such cases, and will set up
a proxy in the target namespace.

elf/dl-load.c: Suppress audit calls when a (new) namespace is empty

When preparing an RTLD_SHARED proxy in a new namespace
it is possible for the target namespace to be empty:

This can happen for RTLD_SHARED + LM_ID_NEWLM.

The audit infrastructure should not be invoked at this
point (as there's nothing there to audit yet).

bits/dlfcn.h, elf/dl-load.c, elf/dl-open.c, elf/rtld.c,
sysdeps/mips/bits/dlfcn.h:
Suppress inter-namespace DSO sharing for audit libraries

Audit libraries should not participate in DSO sharing: In
particular libraries tagged with DF_GNU_1_UNIQUE should not
be shared between the audit namespace and any others - they
should get their own copy.

This is signalled to the loader code by passing the RTLD_ISOLATE
flag from the relevant entry point in the dl modes argument.

elf/dl-sym.c: dlsym, dlvsym must be able to resolve symbols via proxies
---
 bits/dlfcn.h               |  10 ++
 elf/dl-close.c             |  43 +++++----
 elf/dl-deps.c              |  17 ++++
 elf/dl-fini.c              |   6 +-
 elf/dl-init.c              |   4 +-
 elf/dl-load.c              | 181 +++++++++++++++++++++++++++++++++----
 elf/dl-lookup.c            |  26 +++++-
 elf/dl-object.c            |  78 ++++++++++++++++
 elf/dl-open.c              | 121 +++++++++++++++++++++++--
 elf/dl-sym.c               |  14 +++
 elf/rtld.c                 |   2 +-
 include/link.h             |   6 +-
 sysdeps/generic/ldsodefs.h |   7 ++
 sysdeps/mips/bits/dlfcn.h  |  10 ++
 14 files changed, 473 insertions(+), 52 deletions(-)

diff --git a/bits/dlfcn.h b/bits/dlfcn.h
index f3bc63e958..1366cb3546 100644
--- a/bits/dlfcn.h
+++ b/bits/dlfcn.h
@@ -32,6 +32,16 @@
    visible as if the object were linked directly into the program.  */
 #define RTLD_GLOBAL	0x00100
 
+/* If the following bit is set in the MODE argument to dlmopen
+   then the target object is loaded into the main namespace (if
+   it is not already there) and a shallow copy (proxy) is placed
+   in the target namespace: This allows multiple namespaces to
+   share a single instance of a DSO.  */
+#define RTLD_SHARED 0x00080
+
+/* Suppress RTLD_SHARED and/or DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE  */
+#define RTLD_ISOLATE 0x00040
+
 /* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
    The implementation does this by default and so we can define the
    value to zero.  */
diff --git a/elf/dl-close.c b/elf/dl-close.c
index 3720e47dd1..bcdddc75c5 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -284,8 +284,9 @@ _dl_close_worker (struct link_map *map, bool force)
 
 	  /* Call its termination function.  Do not do it for
 	     half-cooked objects.  Temporarily disable exception
-	     handling, so that errors are fatal.  */
-	  if (imap->l_init_called)
+	     handling, so that errors are fatal.
+	     Proxies should never have this flag set, but we double check.  */
+	  if (imap->l_init_called && !imap->l_proxy)
 	    {
 	      /* When debugging print a message first.  */
 	      if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS,
@@ -361,7 +362,9 @@ _dl_close_worker (struct link_map *map, bool force)
 	     one for the terminating NULL pointer.  */
 	  size_t remain = (new_list != NULL) + 1;
 	  bool removed_any = false;
-	  for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt)
+	  for (size_t cnt = 0;
+               imap->l_scope && imap->l_scope[cnt] != NULL;
+               ++cnt)
 	    /* This relies on l_scope[] entries being always set either
 	       to its own l_symbolic_searchlist address, or some map's
 	       l_searchlist address.  */
@@ -689,8 +692,10 @@ _dl_close_worker (struct link_map *map, bool force)
 
 	  /* We can unmap all the maps at once.  We determined the
 	     start address and length when we loaded the object and
-	     the `munmap' call does the rest.  */
-	  DL_UNMAP (imap);
+	     the `munmap' call does the rest. Proxies do not have
+             any segments of their own to unmap.  */
+          if (!imap->l_proxy)
+            DL_UNMAP (imap);
 
 	  /* Finally, unlink the data structure and free it.  */
 #if DL_NNS == 1
@@ -730,19 +735,23 @@ _dl_close_worker (struct link_map *map, bool force)
 	    _dl_debug_printf ("\nfile=%s [%lu];  destroying link map\n",
 			      imap->l_name, imap->l_ns);
 
-	  /* This name always is allocated.  */
-	  free (imap->l_name);
-	  /* Remove the list with all the names of the shared object.  */
+          /* Skip structures borrowed by proxies from the real map.  */
+          if (!imap->l_proxy)
+            {
+              /* This name always is allocated.  */
+              free (imap->l_name);
+              /* Remove the list with all the names of the shared object.  */
 
-	  struct libname_list *lnp = imap->l_libname;
-	  do
-	    {
-	      struct libname_list *this = lnp;
-	      lnp = lnp->next;
-	      if (!this->dont_free)
-		free (this);
-	    }
-	  while (lnp != NULL);
+              struct libname_list *lnp = imap->l_libname;
+              do
+                {
+                  struct libname_list *this = lnp;
+                  lnp = lnp->next;
+                  if (!this->dont_free)
+                    free (this);
+                }
+              while (lnp != NULL);
+            }
 
 	  /* Remove the searchlists.  */
 	  free (imap->l_initfini);
diff --git a/elf/dl-deps.c b/elf/dl-deps.c
index 087a49b212..b06134c512 100644
--- a/elf/dl-deps.c
+++ b/elf/dl-deps.c
@@ -66,6 +66,23 @@ openaux (void *a)
 			       ? lt_library : args->map->l_type),
 			      args->trace_mode, args->open_mode,
 			      args->map->l_ns);
+
+  /* This implies that we need to prepare a proxy in the target namespace.  */
+  if (__glibc_unlikely (args->map->l_ns != LM_ID_BASE &&
+                        args->aux->l_ns == LM_ID_BASE))
+    {
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+        _dl_debug_printf ("need proxy for file=%s [%lu]; initstate=%ld\n\n",
+                          args->aux->l_name, args->aux->l_ns,
+                          (long int)args->aux->l_real->l_relocated);
+
+      args->aux = _dl_new_proxy (args->aux, args->open_mode, args->map->l_ns);
+
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+        _dl_debug_printf ("proxying dependency=%s [%lu]; direct_opencount=%u\n\n",
+                          args->aux->l_name, args->aux->l_ns,
+                          args->aux->l_direct_opencount);
+    }
 }
 
 static ptrdiff_t
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 6dbdfe4b3e..10194488bb 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -73,7 +73,7 @@ _dl_fini (void)
 	  assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
 	  for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
 	    /* Do not handle ld.so in secondary namespaces.  */
-	    if (l == l->l_real)
+	    if (l == l->l_real || l->l_proxy)
 	      {
 		assert (i < nloaded);
 
@@ -111,7 +111,9 @@ _dl_fini (void)
 	    {
 	      struct link_map *l = maps[i];
 
-	      if (l->l_init_called)
+              /* Do not call fini functions via proxies, or for
+                 objects which are not marked as initialised.  */
+	      if (l->l_init_called && !l->l_proxy)
 		{
 		  /* Make sure nothing happens if we are called twice.  */
 		  l->l_init_called = 0;
diff --git a/elf/dl-init.c b/elf/dl-init.c
index f924d26642..00ce8bb9d6 100644
--- a/elf/dl-init.c
+++ b/elf/dl-init.c
@@ -30,8 +30,8 @@ call_init (struct link_map *l, int argc, char **argv, char **env)
      need relocation, and neither do proxy objects.)  */
   assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable);
 
-  if (l->l_init_called)
-    /* This object is all done.  */
+  if (l->l_init_called || l->l_proxy)
+    /* This object is all done, or a proxy (and therefore initless).  */
     return;
 
   /* Avoid handling this constructor again in case we have a circular
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 050c64135a..19b8eb64bc 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -854,6 +854,53 @@ _dl_init_paths (const char *llp, const char *source,
     __rtld_env_path_list.dirs = (void *) -1;
 }
 
+static bool
+has_gnu_unique (int fd, const ElfW(Ehdr) *header, const ElfW(Phdr) *phdr)
+{
+  bool unique = false;
+  const ElfW(Phdr) *ph;
+
+  for (ph = phdr; ph < &phdr[header->e_phnum]; ++ph)
+    {
+      off64_t end;
+      off64_t pos;
+      ssize_t bytes = -1;
+      ElfW(Dyn) entry = { .d_tag = 0, .d_un.d_val = 0 };
+
+      switch (ph->p_type)
+	{
+        case PT_DYNAMIC:
+          pos = ph->p_offset;
+          end = pos + ph->p_filesz;
+
+          while (pos < end)
+            {
+              bytes = __pread64_nocancel (fd, &entry, sizeof (ElfW(Dyn)), pos);
+
+              if (__glibc_unlikely (bytes != sizeof (ElfW(Dyn))))
+                goto done;
+
+              pos += bytes;
+
+              switch (entry.d_tag)
+                {
+                case DT_GNU_FLAGS_1:
+                  unique = (entry.d_un.d_val & DF_GNU_1_UNIQUE);
+                  goto done;
+                  break;
+
+                case DT_NULL:
+                  goto done;
+                  break;
+                }
+            }
+          break;
+        }
+    }
+
+ done:
+  return unique;
+}
 
 /* Process PT_GNU_PROPERTY program header PH in module L after
    PT_LOAD segments are mapped.  Only one NT_GNU_PROPERTY_TYPE_0
@@ -1037,6 +1084,32 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     }
 #endif
 
+  /* DSOs in the main namespace which are flagged DF_GNU_1_UNIQUE should only
+     be opened into the main namespace. Other namespaces should only get
+     proxies.  */
+  if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE)))
+    {
+      /* Check base ns to see if the name matched another already loaded.  */
+      for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL; l = l->l_next)
+        if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
+          {
+            if (!(l->l_gnu_flags_1 & DF_GNU_1_UNIQUE))
+              continue;
+
+            /* Already loaded. Bump its reference count and return it.  */
+            __close_nocancel (fd);
+
+            /* If the name is not listed for this object add it.  */
+            free (realname);
+            add_name_to_object (l, name);
+
+            /* NOTE: It is important that our caller picks up on the fact
+               that we have NOT returned an object in the requested namespace
+               and handles the proxying correctly.  */
+            return l;
+          }
+    }
+
   if (mode & RTLD_NOLOAD)
     {
       /* We are not supposed to load the object unless it is already
@@ -1062,8 +1135,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 	  && __glibc_unlikely (GLRO(dl_naudit) > 0))
 	{
 	  struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
-	  /* Do not call the functions for any auditing object.  */
-	  if (head->l_auditing == 0)
+	  /* Do not call the functions for any auditing object.
+	     Do not try to call auditing functions if the namespace
+	     is currently empty. This can hapen when opening the first
+	     DSO in a new namespace.  */
+	  if ((head != NULL) && (head->l_auditing == 0))
 	    {
 	      struct audit_ifaces *afct = GLRO(dl_audit);
 	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
@@ -1089,6 +1165,32 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   else
     assert (r->r_state == RT_ADD);
 
+  /* Load the ELF header using preallocated struct space if it's big enough.  */
+  maplength = header->e_phnum * sizeof (ElfW(Phdr));
+  if (header->e_phoff + maplength <= (size_t) fbp->len)
+    phdr = (void *) (fbp->buf + header->e_phoff);
+  else
+    {
+      phdr = alloca (maplength);
+      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
+				       header->e_phoff) != maplength)
+	{
+	  errstring = N_("cannot read file data");
+	  goto lose_errno;
+	}
+    }
+
+  /* We need to check for DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE before we start
+     initialising any namespace dependent metatada.  */
+  if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE)))
+    {
+      /* Target DSO is flagged as unique: Make sure it gets loaded into
+         the base namespace.  It is up to our caller to generate a proxy
+         in the target nsid.  */
+      if (has_gnu_unique (fd, header, phdr))
+        nsid = LM_ID_BASE;
+    }
+
   /* Enter the new object in the list of loaded objects.  */
   l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
   if (__glibc_unlikely (l == NULL))
@@ -1106,20 +1208,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   type = header->e_type;
   l->l_phnum = header->e_phnum;
 
-  maplength = header->e_phnum * sizeof (ElfW(Phdr));
-  if (header->e_phoff + maplength <= (size_t) fbp->len)
-    phdr = (void *) (fbp->buf + header->e_phoff);
-  else
-    {
-      phdr = alloca (maplength);
-      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
-				       header->e_phoff) != maplength)
-	{
-	  errstring = N_("cannot read file data");
-	  goto lose_errno;
-	}
-    }
-
    /* On most platforms presume that PT_GNU_STACK is absent and the stack is
     * executable.  Other platforms default to a nonexecutable stack and don't
     * need PT_GNU_STACK to do so.  */
@@ -2027,6 +2115,37 @@ open_path (const char *name, size_t namelen, int mode,
   return -1;
 }
 
+/* Search for a link map proxy in the given namespace by name.
+   Consider it to be an error if the found object is not a proxy.  */
+struct link_map *
+_dl_find_proxy (Lmid_t nsid, const char *name)
+{
+  struct link_map *l;
+
+  for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
+    {
+      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0))
+        continue;
+
+      if (!_dl_name_match_p (name, l))
+        continue;
+
+      /* We have a match - stop searching.  */
+      break;
+    }
+
+  if (l != NULL)
+    {
+      if (l->l_proxy)
+        return l;
+
+      _dl_signal_error (EEXIST, name, NULL,
+                        N_("object cannot be demoted to a proxy"));
+    }
+
+  return NULL;
+}
+
 /* Search for a shared object in a given namespace.  */
 struct link_map *
 _dl_find_dso (const char *name, Lmid_t nsid)
@@ -2080,12 +2199,42 @@ _dl_map_object (struct link_map *loader, const char *name,
 
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
+  assert (!((mode & RTLD_ISOLATE) && (mode & RTLD_SHARED)));
+
+#ifdef SHARED
+  /* Only need to do proxy checks if 'nsid' is not LM_ID_BASE.  */
+  if (__glibc_unlikely ((mode & RTLD_SHARED) && (nsid != LM_ID_BASE)))
+    {
+      /* Search the namespace in case the object is already proxied.  */
+      l = _dl_find_proxy (nsid, name);
+      if (l != NULL)
+        return l;
+
+      /* Further searches should be in the base ns: We will proxy the
+         resulting object in dl_open_worker *after* it is initialised.  */
+      nsid = LM_ID_BASE;
+    }
+#endif
 
   /* Look for this name among those already loaded.  */
   l = _dl_find_dso (name, nsid);
 
   if (l != NULL)
     {
+#ifdef SHARED
+      /* If we are trying to load a DF_GNU_1_UNIQUE flagged DSO which WAS
+         already opened in the target NS but with RTLD_ISOLATE so it WAS NOT
+         created as a proxy we need to error out since we cannot satisfy the
+         DF_GNU_1_UNIQUE is-equivalent-to RTLD_SHARED semantics.  */
+      if (!(mode & RTLD_ISOLATE) &&
+          (l->l_ns != LM_ID_BASE) &&
+          (l->l_gnu_flags_1 & DF_GNU_1_UNIQUE) &&
+          !l->l_proxy)
+      {
+        _dl_signal_error (EEXIST, name, NULL,
+                          N_("object cannot be demoted to a proxy"));
+      }
+#endif
       return l;
     }
 
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index eea217eb28..42effd8e4d 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -24,6 +24,7 @@
 #include <ldsodefs.h>
 #include <dl-hash.h>
 #include <dl-machine.h>
+#include <dl-dst.h>
 #include <sysdep-cancel.h>
 #include <libc-lock.h>
 #include <tls.h>
@@ -917,11 +918,28 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
 	}
     }
 
+  /* Found a candidate symbol but it resides in the base namespace
+     BUT we are in another namespace.  Therefore we want a proxy
+     (which should already have been created by this point.  */
+  if (__glibc_likely ((current_value.m != NULL) &&
+                      !(IS_RTLD (current_value.m))))
+    if (__glibc_unlikely (undef_map->l_ns != LM_ID_BASE &&
+                          current_value.m->l_ns == LM_ID_BASE))
+      {
+        struct link_map *proxy = NULL;
+        proxy = _dl_find_proxy (undef_map->l_ns, current_value.m->l_name);
+
+        if (proxy != NULL)
+          current_value.m = proxy;
+        else
+          _dl_debug_printf ("Failed to find proxy for %s\n", current_value.m->l_name);
+      }
+
   /* We have to check whether this would bind UNDEF_MAP to an object
      in the global scope which was dynamically loaded.  In this case
      we have to prevent the latter from being unloaded unless the
      UNDEF_MAP object is also unloaded.  */
-  if (__glibc_unlikely (current_value.m->l_type == lt_loaded)
+  if (__glibc_unlikely (current_value.m->l_real->l_type == lt_loaded)
       /* Don't do this for explicit lookups as opposed to implicit
 	 runtime lookups.  */
       && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0
@@ -935,8 +953,8 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
 				  version, type_class, flags, skip_map);
 
   /* The object is used.  */
-  if (__glibc_unlikely (current_value.m->l_used == 0))
-    current_value.m->l_used = 1;
+  if (__glibc_unlikely (current_value.m->l_real->l_used == 0))
+    current_value.m->l_real->l_used = 1;
 
   if (__glibc_unlikely (GLRO(dl_debug_mask)
 			& (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK)))
@@ -944,7 +962,7 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
 			&current_value, version, type_class, protected);
 
   *ref = current_value.s;
-  return LOOKUP_VALUE (current_value.m);
+  return LOOKUP_VALUE (current_value.m->l_real);
 }
 
 
diff --git a/elf/dl-object.c b/elf/dl-object.c
index 1875599eb2..30a05b086f 100644
--- a/elf/dl-object.c
+++ b/elf/dl-object.c
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <ldsodefs.h>
+#include <libintl.h>
 
 #include <assert.h>
 
@@ -50,6 +51,83 @@ _dl_add_to_namespace_list (struct link_map *new, Lmid_t nsid)
   __rtld_lock_unlock_recursive (GL(dl_load_write_lock));
 }
 
+/* Proxy an existing link map entry into a new link map:
+   This is based on _dl_new_object, skipping the steps we know we won't need
+   because this is mostly just a shell for the l_real pointer holding the real
+   link map entry (normally l == l->l_real, but not for ld.so in non-main
+   link maps or RTLD_SHARED proxies).
+   It also flags the proxy by setting l_proxy, and sets the the no-delete
+   flag in the original if it is an lt_loaded.  */
+struct link_map *
+_dl_new_proxy (struct link_map *old, int mode, Lmid_t nsid)
+{
+  const char *name;
+  struct link_map *new;
+  struct libname_list *newname;
+#ifdef SHARED
+  unsigned int na = GLRO(dl_naudit);
+
+  if ((mode & __RTLD_OPENEXEC) != 0)
+    na = DL_NNS;
+
+  size_t audit_space = na * sizeof (struct auditstate);
+#else
+# define audit_space 0
+#endif
+
+  name = old->l_name;
+
+  /* Find the original link map entry if 'old' is itself a proxy. */
+  while (old != NULL && old->l_proxy)
+    old = old->l_real;
+
+  if (old == NULL)
+    _dl_signal_error (EINVAL, name, NULL, N_("cannot proxy NULL link_map"));
+
+  /* Object already exists in the target namespace.  This should get handled
+     by dl_open_worker but just in case we get this far, handle it:  */
+  if (__glibc_unlikely (old->l_ns == nsid))
+    _dl_signal_error (EEXIST, name, NULL,
+                      N_("existing object cannot be demoted to a proxy"));
+
+  /* Now duplicate as little of _dl_new_object as possible to get a
+     working proxied object in the target link map.  */
+  new = (struct link_map *) calloc (sizeof (*new) + audit_space
+                                    + sizeof (struct link_map *)
+                                    + sizeof (*newname) + PATH_MAX, 1);
+
+  if (new == NULL)
+    _dl_signal_error (ENOMEM, name, NULL,
+                      N_("cannot create shared object descriptor"));
+
+  /* Specific to the proxy.  */
+  new->l_real = old;
+  new->l_proxy = 1;
+  new->l_ns = nsid;
+
+  /* Copied from the origin.  */
+  new->l_libname = old->l_libname;
+  new->l_name = old->l_name;
+  /* Proxies are considered lt_loaded if the real entry type is lt_library.  */
+  new->l_type = (old->l_type == lt_library) ? lt_loaded : old->l_type;
+
+  if (__glibc_unlikely (mode & RTLD_NODELETE))
+    new->l_flags_1 |= DF_1_NODELETE;
+
+  /* Specific to the origin.  Ideally we'd do some accounting here but
+     for now it's easier to pin the original so the proxy remains valid.  */
+  if (old->l_type == lt_loaded)
+    old->l_flags_1 |= DF_1_NODELETE;
+
+  /* Fix up the searchlist so that relocations work.  */
+  _dl_map_object_deps (new, NULL, 0, 0,
+		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
+
+  /* And finally put the proxy in the target namespace.  */
+  _dl_add_to_namespace_list (new, nsid);
+
+  return new;
+}
 
 /* Allocate a `struct link_map' for a new object being loaded,
    and enter it into the _dl_loaded list.  */
diff --git a/elf/dl-open.c b/elf/dl-open.c
index a066f39bd0..8eed817980 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -488,6 +488,16 @@ dl_open_worker (void *a)
   const char *file = args->file;
   int mode = args->mode;
   struct link_map *call_map = NULL;
+  struct link_map *preloaded = NULL;
+  bool want_proxy = false;
+  bool dl_isolate = mode & RTLD_ISOLATE;
+  Lmid_t proxy_ns = LM_ID_BASE;
+
+  /* Isolation means we should suppress all inter-namespace sharing.  */
+  if (dl_isolate)
+    mode &= ~RTLD_SHARED;
+  else
+    want_proxy = mode & RTLD_SHARED;
 
   /* Determine the caller's map if necessary.  This is needed in case
      we have a DST, when we don't know the namespace ID we have to put
@@ -512,6 +522,15 @@ dl_open_worker (void *a)
 	args->nsid = call_map->l_ns;
     }
 
+  /* Now that we know the NS for sure, sanity check the mode.  */
+  if (__glibc_likely (args->nsid == LM_ID_BASE) &&
+      __glibc_unlikely (mode & RTLD_SHARED))
+    {
+      args->mode &= ~RTLD_SHARED;
+      mode &= ~RTLD_SHARED;
+      want_proxy = 0;
+    }
+
   /* The namespace ID is now known.  Keep track of whether libc.so was
      already loaded, to determine whether it is necessary to call the
      early initialization routine (or clear libc_map on error).  */
@@ -525,6 +544,24 @@ dl_open_worker (void *a)
      may not be true if this is a recursive call to dlopen.  */
   _dl_debug_initialize (0, args->nsid);
 
+  /* Target Lmid is not the base and we haven't explicitly asked for a proxy:
+     We need to check for a matching DSO in the base Lmid in case it is flagged
+     DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE in which case we add RTLD_SHARED to the
+     mode and set want_proxy.
+     NOTE: RTLD_ISOLATE in the mode suppresses this behaviour.  */
+  if (__glibc_unlikely (args->nsid != LM_ID_BASE) &&
+      __glibc_likely (!dl_isolate) &&
+      __glibc_likely (!want_proxy))
+    {
+      preloaded = _dl_find_dso (file, LM_ID_BASE);
+
+      if ((preloaded != NULL) && (preloaded->l_gnu_flags_1 & DF_GNU_1_UNIQUE))
+        {
+          want_proxy = true;
+          mode |= RTLD_SHARED;
+        }
+    }
+
   /* Load the named object.  */
   struct link_map *new;
   args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,
@@ -545,6 +582,35 @@ dl_open_worker (void *a)
   /* This object is directly loaded.  */
   ++new->l_direct_opencount;
 
+  /* Proxy already existed in the target ns, nothing left to do.  */
+  if (__glibc_unlikely (new->l_proxy))
+    {
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+	_dl_debug_printf ("proxied file=%s [%lu]; direct_opencount=%u\n\n",
+			  new->l_name, new->l_ns, new->l_direct_opencount);
+      return;
+    }
+
+  /* If we are trying to load a DF_GNU_1_UNIQUE flagged DSO which was
+     NOT ALREADY LOADED (or not loaded with the name we are using) then
+     _dl_map_object will have returned an instance from the main namespace.
+     We need to detect this and set up the RTLD_SHARED flags.  */
+  if (__glibc_unlikely (args->nsid != LM_ID_BASE && new->l_ns == LM_ID_BASE))
+    {
+      want_proxy = RTLD_SHARED;
+      mode |= RTLD_SHARED;
+    }
+
+  /* If we want proxy and we get this far then the entry in 'new' will
+     be in the main namespace: we should revert to the main namespace code
+     path(s), but remember the namespace we want the proxy to be in.  */
+  if (__glibc_unlikely (want_proxy))
+    {
+      proxy_ns = args->nsid;
+      args->nsid = LM_ID_BASE;
+      args->libc_already_loaded = GL(dl_ns)[LM_ID_BASE].libc_map != NULL;
+    }
+
   /* It was already open.  */
   if (__glibc_unlikely (new->l_searchlist.r_list != NULL))
     {
@@ -576,6 +642,16 @@ dl_open_worker (void *a)
 
       assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
 
+      if (__glibc_unlikely (want_proxy))
+        {
+          args->map = new = _dl_new_proxy (new, mode, proxy_ns);
+          ++new->l_direct_opencount;
+
+          if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
+            _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n",
+                              new->l_name, new->l_ns, new->l_direct_opencount);
+        }
+
       return;
     }
 
@@ -586,7 +662,8 @@ dl_open_worker (void *a)
 
   /* Load that object's dependencies.  */
   _dl_map_object_deps (new, NULL, 0, 0,
-		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
+		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT |
+		               RTLD_ISOLATE));
 
   /* So far, so good.  Now check the versions.  */
   for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
@@ -680,7 +757,10 @@ dl_open_worker (void *a)
     {
       l = new->l_initfini[i];
 
-      if (l->l_real->l_relocated)
+      if (l->l_proxy)
+          l = l->l_real;
+
+      if (l->l_relocated)
 	continue;
 
       if (! relocation_in_progress)
@@ -769,16 +849,21 @@ dl_open_worker (void *a)
      namespace.  */
   if (!args->libc_already_loaded)
     {
+      /* If this is a secondary (nsid != LM_ID_BASE) namespace then
+         it is POSSIBLE there's no libc_map at all - We use the one
+         shared with LM_ID_BASE instead (which MUST already be
+         initialised for us to even reach here).  */
       struct link_map *libc_map = GL(dl_ns)[args->nsid].libc_map;
 #ifdef SHARED
-      bool initial = libc_map->l_ns == LM_ID_BASE;
+      bool initial = (libc_map != NULL) && (libc_map->l_real->l_ns == LM_ID_BASE);
 #else
       /* In the static case, there is only one namespace, but it
 	 contains a secondary libc (the primary libc is statically
 	 linked).  */
       bool initial = false;
 #endif
-      _dl_call_libc_early_init (libc_map, initial);
+      if (libc_map != NULL)
+        _dl_call_libc_early_init (libc_map, initial);
     }
 
   /* Run the initializer functions of new objects.  Temporarily
@@ -799,10 +884,27 @@ dl_open_worker (void *a)
   if (mode & RTLD_GLOBAL)
     add_to_global_update (new);
 
+  if (__glibc_unlikely (want_proxy))
+    {
+      /* args->map is the return slot which the caller sees, but keep
+         the original value of new hanging around so we can see the
+         real link map entry (for logging etc).  */
+      args->map = _dl_new_proxy (new, mode, proxy_ns);
+      ++args->map->l_direct_opencount;
+    }
+
   /* Let the user know about the opencount.  */
   if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
-    _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
-		      new->l_name, new->l_ns, new->l_direct_opencount);
+    {
+      _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
+                        new->l_name, new->l_ns, new->l_direct_opencount);
+
+      if (args->map->l_proxy)
+        _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n",
+                          args->map->l_name,
+                          args->map->l_ns,
+                          args->map->l_direct_opencount);
+    }
 }
 
 void *
@@ -891,8 +993,11 @@ no more namespaces available for dlmopen()"));
   if (__glibc_unlikely (exception.errstring != NULL))
     {
       /* Avoid keeping around a dangling reference to the libc.so link
-	 map in case it has been cached in libc_map.  */
-      if (!args.libc_already_loaded)
+	 map in case it has been cached in libc_map.
+         If this is a secondary namespace opened with LM_ID_NEWLM and
+         WITHOUT RTLD_ISOLATE then nsid can still be -1 (LM_ID_NEWLM)
+         at this point.  */
+      if (!args.libc_already_loaded && nsid >= LM_ID_BASE)
 	GL(dl_ns)[nsid].libc_map = NULL;
 
       /* Remove the object from memory.  It may be in an inconsistent
diff --git a/elf/dl-sym.c b/elf/dl-sym.c
index fa0cce678f..966079915a 100644
--- a/elf/dl-sym.c
+++ b/elf/dl-sym.c
@@ -96,6 +96,10 @@ do_sym (void *handle, const char *name, void *who,
     {
       match = _dl_sym_find_caller_link_map (caller);
 
+      /* Proxies don't contain any symbols: Need to look at the real DSO.  */
+      if (__glibc_unlikely (match->l_proxy))
+	match = match->l_real;
+
       /* Search the global scope.  We have the simple case where
 	 we look up in the scope of an object which was part of
 	 the initial binary.  And then the more complex part
@@ -140,6 +144,11 @@ RTLD_NEXT used in code not dynamically loaded"));
 	}
 
       struct link_map *l = match;
+
+      /* Proxies don't contain any symbols: Need to look at the real DSO.  */
+      if (__glibc_unlikely (l->l_proxy))
+	l = l->l_real;
+
       while (l->l_loader != NULL)
 	l = l->l_loader;
 
@@ -150,6 +159,11 @@ RTLD_NEXT used in code not dynamically loaded"));
     {
       /* Search the scope of the given object.  */
       struct link_map *map = handle;
+
+      /* Proxies don't contain any symbols: Need to look at the real DSO.  */
+      if (__glibc_unlikely (map->l_proxy))
+	map = map->l_real;
+
       result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
 					 vers, 0, flags, NULL);
     }
diff --git a/elf/rtld.c b/elf/rtld.c
index fbbd60b446..9246200be1 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -661,7 +661,7 @@ dlmopen_doit (void *a)
   struct dlmopen_args *args = (struct dlmopen_args *) a;
   args->map = _dl_open (args->fname,
 			(RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT
-			 | __RTLD_SECURE),
+			 | __RTLD_SECURE | RTLD_ISOLATE),
 			dl_main, LM_ID_NEWLM, _dl_argc, _dl_argv,
 			__environ);
 }
diff --git a/include/link.h b/include/link.h
index 6ed35eb808..55e0cad71d 100644
--- a/include/link.h
+++ b/include/link.h
@@ -107,8 +107,9 @@ struct link_map
        They may change without notice.  */
 
     /* This is an element which is only ever different from a pointer to
-       the very same copy of this type for ld.so when it is used in more
-       than one namespace.  */
+       the very same copy of this type when:
+       - A shallow copy of ld.so is placed in namespaces other than LM_ID_BASE.
+       - An object is proxied into a namespace by dlmopen with RTLD_SHARED.  */
     struct link_map *l_real;
 
     /* Number of the namespace this link map belongs to.  */
@@ -180,6 +181,7 @@ struct link_map
     unsigned int l_relocated:1;	/* Nonzero if object's relocations done.  */
     unsigned int l_init_called:1; /* Nonzero if DT_INIT function called.  */
     unsigned int l_global:1;	/* Nonzero if object in _dl_global_scope.  */
+    unsigned int l_proxy:1;    /* Nonzero if object is a shallow copy.  */
     unsigned int l_reserved:2;	/* Reserved for internal use.  */
     unsigned int l_phdr_allocated:1; /* Nonzero if the data structure pointed
 					to by `l_phdr' is allocated.  */
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 44e7097712..66cfc1deb3 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1015,6 +1015,11 @@ extern lookup_t _dl_lookup_symbol_x (const char *undef,
 				     struct link_map *skip_map)
      attribute_hidden;
 
+/* Proxy an existing link map entry into a new link map */
+extern struct link_map *_dl_new_proxy (struct link_map *old,
+				       int mode,
+				       Lmid_t nsid)
+     attribute_hidden;
 
 /* Restricted version of _dl_lookup_symbol_x.  Searches MAP (and only
    MAP) for the symbol UNDEF_NAME, with GNU hash NEW_HASH (computed
@@ -1290,6 +1295,8 @@ rtld_hidden_proto (_dl_find_dso_for_object)
 extern struct link_map *_dl_find_dso (const char *name, Lmid_t nsid);
 rtld_hidden_proto (_dl_find_dso)
 
+extern struct link_map *_dl_find_proxy (Lmid_t nsid, const char *name);
+rtld_hidden_proto (_dl_find_proxy)
 
 /* Initialization which is normally done by the dynamic linker.  */
 extern void _dl_non_dynamic_init (void)
diff --git a/sysdeps/mips/bits/dlfcn.h b/sysdeps/mips/bits/dlfcn.h
index 5cec898de3..898797f863 100644
--- a/sysdeps/mips/bits/dlfcn.h
+++ b/sysdeps/mips/bits/dlfcn.h
@@ -32,6 +32,16 @@
    visible as if the object were linked directly into the program.  */
 #define RTLD_GLOBAL	0x0004
 
+/* If the following bit is set in the MODE argument to dlmopen
+   then the target object is loaded into the main namespace (if
+   it is not already there) and a shallow copy (proxy) is placed
+   in the target namespace: This allows multiple namespaces to
+   share a single instance of a DSO.  */
+#define RTLD_SHARED 0x00020
+
+/* Suppress RTLD_SHARED and/or DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE  */
+#define RTLD_ISOLATE 0x00040
+
 /* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
    The implementation does this by default and so we can define the
    value to zero.  */
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 6/8] Add dlmopen / RTLD_SHARED tests
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
                   ` (4 preceding siblings ...)
  2021-07-08 16:32 ` [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag " Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 20:09   ` Adhemerval Zanella via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 7/8] Restore separate libc loading for the TLS/namespace storage test Vivek Das Mohapatra via Libc-alpha
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

We ensure a DF_GNU_1_UNIQUE library is loaded (as a DT_NEEDED
dependency) into the target namespace by the test module.

This checks that the code path for dependencies honours the flag
and prepares a base namespace library with a proxy in the target
namespace (since the mechanism for this is not the same as for
DSOs that have already been loaded into the base namespace).
---
 elf/Makefile                         | 106 +++-
 elf/tst-dlmopen-auditmod.c           |  23 +
 elf/tst-dlmopen-common.h             |  33 +
 elf/tst-dlmopen-main.h               | 879 +++++++++++++++++++++++++++
 elf/tst-dlmopen-modules.h            |  21 +
 elf/tst-dlmopen-rtld-audit-shared1.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared2.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared3.c |   7 +
 elf/tst-dlmopen-rtld-audit-shared4.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared5.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared6.c |   8 +
 elf/tst-dlmopen-rtld-audit-unique1.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique2.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique3.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique4.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique5.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique6.c |   7 +
 elf/tst-dlmopen-rtld-shared1.c       |   7 +
 elf/tst-dlmopen-rtld-shared1.h       |  64 ++
 elf/tst-dlmopen-rtld-shared2.c       |   7 +
 elf/tst-dlmopen-rtld-shared2.h       |  66 ++
 elf/tst-dlmopen-rtld-shared3.c       |   7 +
 elf/tst-dlmopen-rtld-shared3.h       |  43 ++
 elf/tst-dlmopen-rtld-shared4.c       |   7 +
 elf/tst-dlmopen-rtld-shared4.h       |  14 +
 elf/tst-dlmopen-rtld-shared5.c       |   7 +
 elf/tst-dlmopen-rtld-shared5.h       |  25 +
 elf/tst-dlmopen-rtld-shared6.c       |   7 +
 elf/tst-dlmopen-rtld-shared6.h       |  36 ++
 elf/tst-dlmopen-rtld-unique1.c       |   7 +
 elf/tst-dlmopen-rtld-unique1.h       |  86 +++
 elf/tst-dlmopen-rtld-unique2.c       |   7 +
 elf/tst-dlmopen-rtld-unique2.h       |  25 +
 elf/tst-dlmopen-rtld-unique3.c       |   7 +
 elf/tst-dlmopen-rtld-unique3.h       |  13 +
 elf/tst-dlmopen-rtld-unique4.c       |   7 +
 elf/tst-dlmopen-rtld-unique4.h       |  14 +
 elf/tst-dlmopen-rtld-unique5.c       |   7 +
 elf/tst-dlmopen-rtld-unique5.h       |  58 ++
 elf/tst-dlmopen-rtld-unique6.c       |   7 +
 elf/tst-dlmopen-rtld-unique6.h       |  51 ++
 elf/tst-dlmopen-sharedmod-norm.c     |  34 ++
 elf/tst-dlmopen-sharedmod-uniq.c     |  33 +
 elf/tst-dlmopen-std-do-test.c        |  12 +
 44 files changed, 1808 insertions(+), 1 deletion(-)
 create mode 100644 elf/tst-dlmopen-auditmod.c
 create mode 100644 elf/tst-dlmopen-common.h
 create mode 100644 elf/tst-dlmopen-main.h
 create mode 100644 elf/tst-dlmopen-modules.h
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared1.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared2.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared3.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared4.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared5.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared6.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique1.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique2.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique3.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique4.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique5.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique6.c
 create mode 100644 elf/tst-dlmopen-rtld-shared1.c
 create mode 100644 elf/tst-dlmopen-rtld-shared1.h
 create mode 100644 elf/tst-dlmopen-rtld-shared2.c
 create mode 100644 elf/tst-dlmopen-rtld-shared2.h
 create mode 100644 elf/tst-dlmopen-rtld-shared3.c
 create mode 100644 elf/tst-dlmopen-rtld-shared3.h
 create mode 100644 elf/tst-dlmopen-rtld-shared4.c
 create mode 100644 elf/tst-dlmopen-rtld-shared4.h
 create mode 100644 elf/tst-dlmopen-rtld-shared5.c
 create mode 100644 elf/tst-dlmopen-rtld-shared5.h
 create mode 100644 elf/tst-dlmopen-rtld-shared6.c
 create mode 100644 elf/tst-dlmopen-rtld-shared6.h
 create mode 100644 elf/tst-dlmopen-rtld-unique1.c
 create mode 100644 elf/tst-dlmopen-rtld-unique1.h
 create mode 100644 elf/tst-dlmopen-rtld-unique2.c
 create mode 100644 elf/tst-dlmopen-rtld-unique2.h
 create mode 100644 elf/tst-dlmopen-rtld-unique3.c
 create mode 100644 elf/tst-dlmopen-rtld-unique3.h
 create mode 100644 elf/tst-dlmopen-rtld-unique4.c
 create mode 100644 elf/tst-dlmopen-rtld-unique4.h
 create mode 100644 elf/tst-dlmopen-rtld-unique5.c
 create mode 100644 elf/tst-dlmopen-rtld-unique5.h
 create mode 100644 elf/tst-dlmopen-rtld-unique6.c
 create mode 100644 elf/tst-dlmopen-rtld-unique6.h
 create mode 100644 elf/tst-dlmopen-sharedmod-norm.c
 create mode 100644 elf/tst-dlmopen-sharedmod-uniq.c
 create mode 100644 elf/tst-dlmopen-std-do-test.c

diff --git a/elf/Makefile b/elf/Makefile
index 9734030c23..4cfd79066a 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -196,6 +196,38 @@ static-dlopen-environment = \
 tst-tls9-static-ENV = $(static-dlopen-environment)
 tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment)
 
+dlmopen-rtld-tests-norm := \
+	 tst-dlmopen-rtld-shared1 \
+	 tst-dlmopen-rtld-shared2 \
+	 tst-dlmopen-rtld-shared3 \
+	 tst-dlmopen-rtld-shared4 \
+	 tst-dlmopen-rtld-shared5 \
+	 tst-dlmopen-rtld-shared6
+
+dlmopen-rtld-tests-uniq := \
+	 tst-dlmopen-rtld-unique1 \
+	 tst-dlmopen-rtld-unique2 \
+	 tst-dlmopen-rtld-unique3 \
+	 tst-dlmopen-rtld-unique4 \
+	 tst-dlmopen-rtld-unique5 \
+	 tst-dlmopen-rtld-unique6
+
+dlmopen-rtld-audit-tests-norm := \
+	 tst-dlmopen-rtld-audit-shared1 \
+	 tst-dlmopen-rtld-audit-shared2 \
+	 tst-dlmopen-rtld-audit-shared3 \
+	 tst-dlmopen-rtld-audit-shared4 \
+	 tst-dlmopen-rtld-audit-shared5 \
+	 tst-dlmopen-rtld-audit-shared6
+
+dlmopen-rtld-audit-tests-uniq := \
+	 tst-dlmopen-rtld-audit-unique1 \
+	 tst-dlmopen-rtld-audit-unique2 \
+	 tst-dlmopen-rtld-audit-unique3 \
+	 tst-dlmopen-rtld-audit-unique4 \
+	 tst-dlmopen-rtld-audit-unique5 \
+	 tst-dlmopen-rtld-audit-unique6
+
 tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 constload1 order noload filter \
 	 reldep reldep2 reldep3 reldep4 nodelete nodelete2 \
@@ -233,7 +265,11 @@ tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
 	 tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
 	 tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
-	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
+	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split \
+	 $(dlmopen-rtld-tests-norm) \
+	 $(dlmopen-rtld-tests-uniq) \
+	 $(dlmopen-rtld-audit-tests-norm) \
+	 $(dlmopen-rtld-audit-tests-uniq)
 tests-container += tst-pldd tst-dlopen-tlsmodid-container \
   tst-dlopen-self-container tst-preload-pthread-libc
 test-srcs = tst-pathopt
@@ -288,6 +324,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		$(modules-execstack-$(have-z-execstack)) \
 		tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \
 		tst-dlmopen1mod tst-auditmod1 \
+		tst-dlmopen-sharedmod-norm \
+		tst-dlmopen-sharedmod-uniq \
+		tst-dlmopen-auditmod \
 		unload3mod1 unload3mod2 unload3mod3 unload3mod4 \
 		unload4mod1 unload4mod2 unload4mod3 unload4mod4 \
 		unload6mod1 unload6mod2 unload6mod3 \
@@ -327,6 +366,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		tst-tlsalign-lib tst-nodelete-opened-lib tst-nodelete2mod \
 		tst-audit11mod1 tst-audit11mod2 tst-auditmod11 \
 		tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12 \
+		tst-dlmopen-auditmod \
 		tst-latepthreadmod $(tst-tls-many-dynamic-modules) \
 		tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
 		tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
@@ -819,6 +859,9 @@ tst-nodelete-uniquemod.so-no-z-defs = yes
 tst-nodelete-rtldmod.so-no-z-defs = yes
 tst-nodelete-zmod.so-no-z-defs = yes
 tst-nodelete2mod.so-no-z-defs = yes
+tst-dlmopen-sharedmod-norm.so-no-z-defs = yes
+tst-dlmopen-sharedmod-uniq.so-no-z-defs = yes
+tst-dlmopen-auditmod.so-no-z-defs = yes
 
 ifeq ($(build-shared),yes)
 # Build all the modules even when not actually running test programs.
@@ -1235,6 +1278,67 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
 
 $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
 
+ifneq ($(ld-zunique),yes)
+$(objpfx)tst-dlmopen-sharedmod-uniq.so: $(common-objpfx)elf/dynamic-notes.os
+else
+LDFLAGS-tst-dlmopen-sharedmod-uniq.so = -Wl,-z,unique
+endif
+
+# Add a DT_NEEDED entry for a DF_GNU_1_UNIQUE library to the test modules.
+LDFLAGS-tst-dlmopen-sharedmod-norm.so += -Wl,-lrt
+LDFLAGS-tst-dlmopen-sharedmod-uniq.so += -Wl,-lrt
+
+$(objpfx)tst-dlmopen-sharedmod-norm.so: $(libdl)
+$(objpfx)tst-dlmopen-sharedmod-uniq.so: $(libdl)
+$(objpfx)tst-dlmopen-auditmod.so: $(libdl)
+
+dlmopen-rtld-tests-norm-executables := \
+	 $(foreach x,$(dlmopen-rtld-tests-norm),$(objpfx)$(x))
+dlmopen-rtld-tests-norm-out := \
+	 $(foreach x,$(dlmopen-rtld-tests-norm),$(objpfx)$(x).out)
+
+dlmopen-rtld-tests-uniq-executables := \
+	 $(foreach x,$(dlmopen-rtld-tests-uniq),$(objpfx)$(x))
+dlmopen-rtld-tests-uniq-out := \
+	 $(foreach x,$(dlmopen-rtld-tests-uniq),$(objpfx)$(x).out)
+
+dlmopen-rtld-audit-tests-norm-executables := \
+	 $(foreach x,$(dlmopen-rtld-audit-tests-norm),$(objpfx)$(x))
+dlmopen-rtld-audit-tests-norm-out := \
+	 $(foreach x,$(dlmopen-rtld-audit-tests-norm),$(objpfx)$(x).out)
+
+dlmopen-rtld-audit-tests-uniq-executables := \
+	 $(foreach x,$(dlmopen-rtld-audit-tests-uniq),$(objpfx)$(x))
+dlmopen-rtld-audit-tests-uniq-out := \
+	 $(foreach x,$(dlmopen-rtld-audit-tests-uniq),$(objpfx)$(x).out)
+
+$(dlmopen-rtld-tests-norm-executables): $(libdl)
+$(dlmopen-rtld-tests-norm-out): $(objpfx)tst-dlmopen-sharedmod-norm.so
+
+$(dlmopen-rtld-tests-uniq-executables): $(libdl)
+$(dlmopen-rtld-tests-uniq-out): $(objpfx)tst-dlmopen-sharedmod-uniq.so
+
+
+$(dlmopen-rtld-audit-tests-norm-executables): $(libdl)
+$(dlmopen-rtld-audit-tests-norm-out): $(objpfx)tst-dlmopen-sharedmod-norm.so
+$(dlmopen-rtld-audit-tests-norm-out): $(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-shared1-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-shared2-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-shared3-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-shared4-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-shared5-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-shared6-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+
+$(dlmopen-rtld-audit-tests-uniq-executables): $(libdl)
+$(dlmopen-rtld-audit-tests-uniq-out): $(objpfx)tst-dlmopen-sharedmod-uniq.so
+$(dlmopen-rtld-audit-tests-uniq-out): $(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-unique1-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-unique2-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-unique3-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-unique4-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-unique5-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+tst-dlmopen-rtld-audit-unique6-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
+
 $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
 tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
 
diff --git a/elf/tst-dlmopen-auditmod.c b/elf/tst-dlmopen-auditmod.c
new file mode 100644
index 0000000000..d630a26cd6
--- /dev/null
+++ b/elf/tst-dlmopen-auditmod.c
@@ -0,0 +1,23 @@
+/* Audit module for tst-dlmopen-rtld-audit-*
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+unsigned int
+la_version (unsigned int version)
+{
+  return version;
+}
diff --git a/elf/tst-dlmopen-common.h b/elf/tst-dlmopen-common.h
new file mode 100644
index 0000000000..477e2e30b5
--- /dev/null
+++ b/elf/tst-dlmopen-common.h
@@ -0,0 +1,33 @@
+/* Common infrastructure for tst-dlmopen-rtld-*
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <gnu/lib-names.h>
+
+typedef struct
+{
+  const char *name;
+  void *free;
+  void *timer_create;
+} dlmopen_testresult;
+
+typedef dlmopen_testresult * (*dlmopen_testfunc) (void);
diff --git a/elf/tst-dlmopen-main.h b/elf/tst-dlmopen-main.h
new file mode 100644
index 0000000000..8baf160d6c
--- /dev/null
+++ b/elf/tst-dlmopen-main.h
@@ -0,0 +1,879 @@
+/* Main infrastructure for tst-dlmopen-rtld-*
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+/* dlmopen +/- RTLD_SHARED/RTLD_ISOLATE semantics:
+
+   RTLD_ISOLATE's purpose is to suppress all shared behaviour,
+   mainly used for LD_AUDIT code paths but available to the user
+   and also useful for constructing test case preconditions.
+
+   dlmopen should have the following behaviour:
+
+   Notation:
+        Number of namespace ('+' means make a new one in an empty NS)
+        |
+     [+]X[p] - p indicates a proxy
+      |
+      + -> new enry after the dlmopen call
+
+      Need to be able to inspect:
+
+      list of dl handles before we start (base state)
+      list of dl handles after an action in each namespace
+      ns of a given dl handle
+      _real_ ns of a given handle (ie where does a proxy point)
+
+      In the tables below each line represents a separate test.
+
+      In practice many tests can be implemented in the same executable
+      as some tests set up the required preconditions for other tests
+      if they complete successfully.
+
+      Target is a normal DSO:
+      Before | Target NS | RTLD Flags | After  | handle NS | libc NS
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-shared1:
+      -------+-----------+------------+--------+-----------+---------
+       -     | 0         | -          | +0     | 0         | 0
+       0     | 0         | -          | 0      | 0         | 0
+       0     | 0         | SHARED     | 0      | 0         | 0
+       0     | +         | -          | 0,+1   | 1         | 0
+       0,1   | 0         | -          | 0,1    | 0         | 0
+       0,1   | 0         | SHARED     | 0,1    | 0         | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-shared2:
+      -------+-----------+------------+--------+-----------+---------
+       -     | 0         | SHARED     | +0     | 0         | 0
+       0     | +         | SHARED     | 0,+1p  | 1p        | 0
+       0,1p  | 0         | -          | 0,1p   | 0         | 0
+       0,1p  | 0         | SHARED     | 0,1p   | 0         | 0
+       0,1p  | 1         | -          | 0,1p   | 1p        | 0
+       0,1p  | 1         | SHARED     | 0,1p   | 1p        | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-shared3
+      -------+-----------+------------+--------+-----------+---------
+       -     | +         | -          | +1     | 1         | 0
+       1     | 0         | -          | +0,1   | 0         | 0
+       0,1   | 1         | -          | 0,1    | 1         | 0
+       0,1   | 1         | SHARED     | 0,1    | ERR       | -
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-shared4
+      -------+-----------+------------+--------+-----------+---------
+       -     | +         | SHARED     | +0,+1p | 1p        | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-shared5
+      -------+-----------+------------+--------+-----------+---------
+       1     | 0         | SHARED     | +0,1   | 0         | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-shared6
+      -------+-----------+------------+--------+-----------+---------
+       1     | 1         | -          | 1      | 0         | 0
+       1     | 1         | SHARED     | 1      | ERR       | -
+
+      Target is a DF_GNU_1_UNIQUE DSO:
+      Before | Target NS | RTLD Flags | After  | handle NS | libc NS
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-unique1:
+      -------+-----------+------------+--------+-----------+---------
+       -     | 0         | -          | +0     | 0         | 0
+       0     | 0         | -          | 0      | 0         | 0
+       0     | 0         | SHARED     | 0      | 0         | 0
+       0     | +         | -          | 0,+1p  | 1p        | 0
+       0,1p  | 0         | -          | 0,1p   | 0         | 0
+       0,1p  | 0         | SHARED     | 0,1p   | 0         | 0
+       0,1p  | 1         | -          | 0,1p   | 1p        | 0
+       0,1p  | 1         | SHARED     | 0,1p   | 1p        | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-unique2:
+      -------+-----------+------------+--------+-----------+---------
+       -     | 0         | SHARED     | +0     | 0         | 0
+       0     | +         | SHARED     | 0,+1p  | 1p        | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-unique3:
+      -------+-----------+------------+--------+-----------+---------
+       -     | +         | -          | +0,+1p | 1p        | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-unique4:
+      -------+-----------+------------+--------+-----------+---------
+       -     | +         | SHARED     | +0,+1p | 1p        | 0
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-unique5:
+      -------+-----------+------------+--------+-----------+---------
+       -     | +         | ISOLATE    | +1     | 1         | 1
+       1     | 0         | -          | +0,1   | 0         | 0
+       0,1   | 0         | -          | 0,1    | 0         | 0
+       0,1   | 0         | SHARED     | 0,1    | 0         | 0
+       0,1   | 1         | -          | 0,1    | ERR       | -
+      =======+===========+============+========+===========+=========
+      dlmopen-rtld-unique6:
+      -------+-----------+------------+--------+-----------+---------
+       -     | +1        | ISOLATE    | +1     | 1         | 1
+       1     | 1         | -          | 1      | ERR       | -
+       1     | 1         | SHARED     | 1      | ERR       | -
+       1     | 0         | SHARED     | +0,1   | 0         | 0
+       0,1   | 1         | SHARED     | 0,1    | ERR       | -
+*/
+
+#include "tst-dlmopen-common.h"
+#include <dl-dtprocnum.h>
+#include <link.h>
+#include <array_length.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+#define DSO_NORMAL "$ORIGIN/tst-dlmopen-sharedmod-norm.so"
+#define DSO_UNIQUE "$ORIGIN/tst-dlmopen-sharedmod-uniq.so"
+#define DSO_TESTFN "rtld_shared_testfunc"
+#define DSO_NAMESTUB "tst-dlmopen-sharedmod-"
+#define MAX_NS 16
+
+#define ERROR(test, fmt, ...) \
+  support_print_failure_impl (__FILE__, __LINE__, "%s (%s): " fmt, \
+                              test->name, test->desc, ##__VA_ARGS__)
+
+typedef enum
+  {
+   NONE  = 0,
+   DSO   = 1,
+   PROXY = 2,
+   NEW   = 4,
+  } dso_type;
+
+typedef struct
+{
+  const char *name;
+  const char *desc;
+  int is_prep_stage;
+  const char *dso_name;
+  int failure;
+
+  struct
+  {
+    const char *dso_path;
+    Lmid_t ns;
+    int flags;
+  } args;
+
+  dso_type preloaded[MAX_NS];
+  dso_type loaded[MAX_NS];
+  dso_type handle_type;
+  Lmid_t handle_ns;
+  Lmid_t free_ns;
+} dlmopen_test_spec;
+
+typedef struct { void *handle; Lmid_t ns; } test_handle;
+static test_handle cached_handles[32];
+
+static void
+cache_test_handle (void *handle, Lmid_t ns)
+{
+  int i;
+
+  for (i = 0; i < array_length(cached_handles); i++)
+    if (cached_handles[i].handle == NULL)
+      {
+        cached_handles[i].handle = handle;
+        cached_handles[i].ns = ns;
+        break;
+      }
+}
+
+__attribute__((unused))
+static struct link_map *
+link_map_of_dl_handle (void *handle)
+{
+  struct link_map *lm = NULL;
+
+  if (dlinfo (handle, RTLD_DI_LINKMAP, &lm) == 0)
+    return lm;
+
+  printf ("dlinfo (LINKMAP) for %s in %s failed: %s\n",
+          LIBC_SO, __func__, dlerror ());
+
+  return NULL;
+}
+
+__attribute__((unused))
+static Lmid_t
+ns_of_dl_handle (void *handle)
+{
+  Lmid_t ns = 0;
+
+  if (dlinfo (handle, RTLD_DI_LMID, &ns) == 0)
+    return ns;
+
+  printf ("dlinfo (LMID) for %s in %s failed: %s\n",
+          LIBC_SO, __func__, dlerror ());
+
+  return -1;
+}
+
+__attribute__((unused))
+static Lmid_t real_ns_of_dl_handle (void *handle)
+{
+  Lmid_t ns = 0;
+  struct link_map *lm = link_map_of_dl_handle (handle);
+
+  if (lm == NULL)
+    return -1;
+
+  if (lm->l_proxy)
+    ns = ns_of_dl_handle ((void *) lm->l_real);
+  else
+    ns = ns_of_dl_handle (handle);
+
+  return ns;
+}
+
+__attribute__((unused))
+static const char *str_soname (const char *name)
+{
+  char *slash = NULL;
+
+  if (name == NULL)
+    return NULL;
+
+  if ((slash = strrchr (name, '/')))
+    return ++slash;
+  else
+    return name;
+}
+
+__attribute__((unused))
+static const char *lm_name (struct link_map *lm)
+{
+  if (lm)
+    return lm->l_name;
+
+  return NULL;
+}
+
+
+static int dlm_dso_is_loaded (void *handle)
+{
+  if (handle != RTLD_DEFAULT)
+    {
+      Dl_info sinfo = {};
+
+      /* If it wasn't dynamically loaded it can be of no interest to our test(s).  */
+      if (((struct link_map *) handle)->l_type != lt_loaded)
+        return 0;
+
+      /* Skip link map entries that have not yet been promoted to dl*foo* handles.  */
+      if (((struct link_map *) handle)->l_searchlist.r_list  == NULL ||
+          ((struct link_map *) handle)->l_searchlist.r_nlist == 0)
+        return 0;
+
+      verbose_printf ("checking %p %s for %s\n",
+                      handle, ((struct link_map *)handle)->l_name, DSO_TESTFN);
+
+      void *symbol = dlsym (handle, DSO_TESTFN);
+
+      dladdr (symbol, &sinfo);
+      verbose_printf ("  -> %s (in %s (%p))\n",
+                      sinfo.dli_fname,
+                      sinfo.dli_sname,
+                      sinfo.dli_saddr);
+
+      return symbol ? 1 : 0;
+    }
+
+  for (int i = 0; i < array_length(cached_handles); i++)
+    {
+      if (cached_handles[i].handle == NULL)
+        break;
+
+      if (((struct link_map *) cached_handles[i].handle)->l_type != lt_loaded)
+        continue;
+
+      verbose_printf ("checking %p %s for %s\n",
+                      cached_handles[i].handle,
+                      ((struct link_map *)cached_handles[i].handle)->l_name,
+                      DSO_TESTFN);
+
+      if (dlsym (cached_handles[i].handle, DSO_TESTFN) != NULL)
+        return 1;
+    }
+
+  return 0;
+}
+
+static int
+call_testfunc (dlmopen_test_spec *test, void *handle)
+{
+  Dl_info dli = {};
+  struct link_map *lm = NULL;
+  dlmopen_testfunc func = NULL;
+  dlmopen_testresult *result = NULL;
+
+  if (handle != RTLD_DEFAULT)
+    func = dlsym (handle, DSO_TESTFN);
+
+  if (func == NULL)
+    {
+      ERROR (test, "test function %s not found\n", DSO_TESTFN);
+      return 0;
+    }
+
+  result = (func)();
+
+  if (result == NULL)
+    {
+      ERROR (test, "test function %s returned NULL\n", DSO_TESTFN);
+      return 0;
+    }
+
+  dladdr1 (result->free, &dli, (void **)&lm, RTLD_DL_LINKMAP);
+
+  if (lm == NULL)
+    {
+      ERROR (test, "free() implementation from test module is invalid\n");
+      return 0;
+    }
+
+  if (lm->l_ns != test->free_ns)
+    {
+      ERROR (test,
+             "free() function from test module was from ns %d, expected %d\n",
+             (int)lm->l_ns, (int)test->free_ns);
+      return 0;
+    }
+
+  printf ("%s: %s: %s in ns %d using free() from ns %d: OK\n",
+          test->name, test->args.dso_path, DSO_TESTFN,
+          (int)test->handle_ns, (int)lm->l_ns);
+
+  return 1;
+}
+
+static void *link_map_snapshot_array (void *handle, void *func, Lmid_t ns, size_t *len)
+{
+  struct link_map *lm = NULL;
+  struct link_map *start = NULL;
+
+  if (len != NULL)
+    *len = 0;
+
+  if (handle != NULL)
+    {
+      dlinfo (handle, RTLD_DI_LINKMAP, &lm);
+    }
+  else if (func != NULL)
+    {
+      Dl_info dli = {};
+
+      dladdr1 (func, &dli, (void **)&lm, RTLD_DL_LINKMAP);
+    }
+  else if (ns >= LM_ID_BASE)
+    {
+      for (int i = 0; i < array_length(cached_handles); i++)
+        {
+          if (cached_handles[i].handle == NULL)
+            break;
+
+          if (cached_handles[i].ns != ns)
+            continue;
+
+          dlinfo (cached_handles[i].handle, RTLD_DI_LINKMAP, &lm);
+          break;
+        }
+    }
+
+  if (lm == NULL)
+    return NULL;
+
+  start = lm;
+
+  while (start->l_prev)
+    start = start->l_prev;
+
+  size_t lm_size = 0;
+
+  for (lm = start; lm; lm = lm->l_next)
+    lm_size++;
+
+  struct link_map **lm_list = xcalloc (lm_size + 1, sizeof (struct link_map *));
+
+  if (len != NULL)
+    *len = lm_size;
+
+  int i = 0;
+
+  for (lm = start; lm; lm = lm->l_next)
+    lm_list[i++] = lm;
+  lm_list[i] = NULL;
+
+  return lm_list;
+}
+
+__attribute__((unused))
+static int search_link_map_array (struct link_map **lma, size_t len, void *handle)
+{
+  if (lma == NULL)
+    return 0;
+
+  if (len == 0)
+    return 0;
+
+  struct link_map *target = link_map_of_dl_handle (handle);
+
+  for (int i = 0; i < len; i++)
+    {
+      if (handle == (struct link_map *)(lma[i]))
+        return 1;
+
+      if (target->l_proxy)
+        if (target->l_real == (struct link_map *)(lma[i]))
+          return 1;
+    }
+
+  return 0;
+}
+
+#ifdef DEBUG_DSO_SEARCH
+#define TRACE2(fmt, ...) \
+  printf ("  find-test-dso (%s @ %d): " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
+#else
+#define TRACE2(fmt, ...)
+#endif
+
+static struct link_map *
+find_test_dso_in_link_map_array (struct link_map **lma, size_t len)
+{
+  if (lma == NULL)
+    return NULL;
+
+  if (len == 0)
+    return NULL;
+
+  for (int i = 0; i < len; i++)
+    {
+      if ((lma[i] == NULL) || (lma[i]->l_name == NULL))
+        continue;
+
+      TRACE2 ("%p [%d/%d] %s", lma, i, (int)len - 1,
+              lma[i] ? (lma[i]->l_name ?: "???.so") : "NULL" );
+
+      if (lma[i] && lma[i]->l_name)
+        if (strstr (lma[i]->l_name, DSO_NAMESTUB))
+          if (dlsym (lma[i], DSO_TESTFN) != NULL)
+            return (struct link_map *)lma[i];
+    }
+
+  return NULL;
+}
+
+__attribute__((unused))
+static void *link_map_list (void *handle, void *func, int *len, Lmid_t *ns)
+{
+  struct link_map *lm = NULL;
+  struct link_map *start = NULL;
+
+  if (len)
+    *len = 0;
+
+  if (handle != NULL)
+    {
+      dlinfo (handle, RTLD_DI_LINKMAP, &lm);
+    }
+  else if (func != NULL)
+    {
+      Dl_info dli = {};
+
+      dladdr1 (func, &dli, (void **)&lm, RTLD_DL_LINKMAP);
+    }
+
+  if (lm == NULL)
+    return NULL;
+
+  if (ns)
+    *ns = lm->l_ns;
+
+  /* Rewind to start of link map list:  */
+  start = lm;
+
+  while (start->l_prev != NULL)
+    start = start->l_prev;
+
+  size_t lm_size = 0;
+
+  for (lm = start; lm != NULL; lm = lm->l_next)
+    lm_size++;
+
+  if (len != NULL)
+    *len = lm_size;
+
+  return start;
+}
+
+#ifdef DEBUG_DLMOPEN_TEST_WRAPPER
+#define TRACE(fmt, ...) \
+  printf ("%s (%s @ %d): " fmt "\n", test->name, __FILE__, __LINE__, ##__VA_ARGS__)
+#else
+#define TRACE(fmt, ...)
+#endif
+
+static bool process_test_spec (dlmopen_test_spec *test)
+{
+  void *handle = NULL;
+  size_t lm_before_len[MAX_NS] = { 0 };
+  size_t lm_after_len[MAX_NS] = { 0 };
+  struct link_map **lm_before[MAX_NS] = { NULL };
+  struct link_map **lm_after[MAX_NS] = { NULL };
+  struct link_map *preloads[MAX_NS] = { NULL };
+  int want_preload = 0;
+  int test_status = false;
+
+  TRACE("LD_AUDIT = %s", getenv("LD_AUDIT") ?: "-");
+  TRACE("BEFORE SNAPSHOTS: %p", &lm_before[0]);
+  TRACE("AFTER  SNAPSHOTS: %p", &lm_after[0]);
+  TRACE("preloads        : %p", preloads);
+  TRACE("setup done");
+
+  if (test->args.dso_path && *test->args.dso_path && !test->dso_name)
+    test->dso_name = str_soname (test->args.dso_path);
+
+  TRACE("DSO short name: %s", test->dso_name);
+
+  /* Get the existing link map contents before the test runs:  */
+  lm_before[0] = link_map_snapshot_array (NULL, process_test_spec,
+                                          LM_ID_BASE, &lm_before_len[0]);
+  for (int i = 1; i < MAX_NS; i++)
+    lm_before[i] = link_map_snapshot_array (NULL, NULL, i, &lm_before_len[i]);
+
+  TRACE("link map snapshots cached");
+
+  for (int i = 0; i < MAX_NS; i++)
+    {
+      if (test->preloaded[i] & PROXY)
+        {
+          struct link_map **lm = lm_before[i];
+          want_preload++;
+
+          if (lm != NULL)
+            for (int j = 0; (preloads[i] == NULL) && (j < lm_before_len[i]); j++)
+              if (dlm_dso_is_loaded (lm[j]) && lm[j]->l_proxy)
+                preloads[i] = lm[j];
+
+          if (preloads[i] == NULL)
+            {
+              ERROR (test,
+                     "needed proxy for %s preloaded in NS %d, not found\n",
+                     test->dso_name, i);
+              goto cleanup;
+            }
+        }
+      else if (test->preloaded[i] & DSO)
+        {
+          struct link_map **lm = lm_before[i];
+          int lm_max = lm_before_len[i];
+          want_preload++;
+
+          if (lm != NULL)
+            for (int j = 0; !preloads[i] && (j < lm_max); j++)
+              {
+                if (dlm_dso_is_loaded (lm[j]) && !lm[j]->l_proxy)
+                  preloads[i] = lm[j];
+              }
+          if (!preloads[i])
+            {
+              ERROR (test,
+                     "needed %s preloaded in NS %d, not found\n",
+                     test->dso_name, i);
+              goto cleanup;
+            }
+        }
+    }
+  TRACE("preload checks (A)");
+
+  if (dlm_dso_is_loaded (RTLD_DEFAULT))
+    {
+      /* Test DSO module must _not_ be preloaded, and is:  */
+      if (!want_preload)
+        {
+          ERROR (test, "DSO %s unexpectedly loaded before test\n", test->dso_name);
+          goto cleanup;
+        }
+    }
+  else
+    {
+      /* DSO is not loaded, and must be.  In theory we can never see
+         this error as it should be caught by the preceding preload loop.  */
+      if (want_preload)
+        {
+          ERROR (test, "DSO %s must be preloaded (and is not)\n", test->args.dso_path);
+          goto cleanup;
+        }
+    }
+  TRACE("preload checks (B) %s", test->name);
+
+  if (!(test->args.flags & (RTLD_NOW|RTLD_LAZY)))
+    test->args.flags |= RTLD_NOW;
+
+  handle = dlmopen (test->args.ns, test->args.dso_path, test->args.flags);
+  TRACE("dlmopen returned %p", handle);
+
+  if (handle == NULL)
+    {
+      const char *status = "failed";
+      const char *plabel = "";
+
+      if (test->failure)
+        {
+          test_status = true;
+
+          printf ("%s: dlmopen(%s, %d, 0x%0x) failed: OK (EXPECTED)\n",
+                  test->name, test->args.dso_path,
+                  (int)test->args.ns, (int)test->args.flags);
+          printf ("Returned: %p\n\n", handle);
+
+          goto cleanup;
+        }
+
+      if (test->is_prep_stage)
+        plabel = "(during setup of preconditions): ";
+
+      if (test->args.ns == LM_ID_BASE)
+        ERROR (test, "%sdlmopen (LM_ID_BASE, \"%s\", 0x%x) %s: %s\n",
+               plabel, test->args.dso_path, test->args.flags, status, dlerror ());
+      else
+        ERROR (test, "%sdlmopen (%d, \"%s\", 0x%x) %s: %s\n",
+               plabel, (int)test->args.ns, test->args.dso_path, test->args.flags, status,
+               dlerror ());
+
+      goto cleanup;
+    }
+  else if (test->failure)
+    {
+      ERROR (test, "dlmopen() call should have failed, but did not\n");
+      goto cleanup;
+    }
+
+  TRACE("return status checked");
+
+  if (!dlm_dso_is_loaded (handle))
+    {
+      ERROR (test, "DSO %s (%p) missing function (%s)\n",
+             test->args.dso_path, handle, DSO_TESTFN);
+      goto cleanup;
+    }
+
+  TRACE ("loaded DSO sanity checked");
+
+  Lmid_t hns = ns_of_dl_handle (handle);
+  Lmid_t real_hns = real_ns_of_dl_handle (handle);
+  Lmid_t proxy_ns = 0;
+
+  call_testfunc (test, handle);
+  TRACE (DSO_TESTFN "called");
+
+  cache_test_handle (handle, hns);
+  TRACE ("handle %p cached (ns %d)", handle, (int)hns);
+
+  /* If the real ns was different to the apparent one
+     then we have a proxy and we need to shuffle the values,
+     else leave the proxy ns as 0 as an expect.proxy_ns of 0
+     means we weren't expecting a proxy:  */
+  if (real_hns != hns)
+    {
+      proxy_ns = hns;
+      hns = real_hns;
+    }
+
+  if (proxy_ns)
+    printf ("Returned: proxy ns:%d (real ns: %d)\n\n", (int)proxy_ns, (int)hns);
+  else
+    printf ("Returned: dso ns:%d\n\n", (int)hns);
+
+  Lmid_t expected;
+  if (test->handle_type & PROXY)
+    expected = proxy_ns;
+  else
+    expected = hns;
+
+  TRACE("check expected ns %d", (int)expected);
+
+  if (test->args.ns == LM_ID_NEWLM)
+    {
+      if (expected <= LM_ID_BASE)
+        {
+          ERROR (test, "DSO should have been in NS > %d, was in %d\n",
+                 LM_ID_BASE, (int)expected);
+          goto cleanup;
+        }
+
+      /* For any cases where we can't predict the namespace in advance:  */
+      if (test->handle_ns == LM_ID_NEWLM)
+        test->handle_ns = expected;
+    }
+  else
+    {
+      if (test->args.ns != expected)
+        {
+          ERROR (test, "DSO should have been in NS %d, was in %d\n",
+                 (int)test->args.ns, (int)expected);
+          goto cleanup;
+        }
+    }
+
+  TRACE("ns %d Ok", (int)expected);
+
+if (test->handle_type & PROXY) /* Expecting a proxy.  */
+    {
+      if (proxy_ns != 0) /* Got a proxy.  */
+        {
+          if (test->handle_ns != proxy_ns) /* But not in the right place.  */
+            {
+              ERROR (test, "DSO proxy should have been in ns %d, was in %d\n",
+                     (int)test->handle_ns, (int)proxy_ns);
+              goto cleanup;
+            }
+        }
+      else /* Didn't get a proxy.  */
+        {
+          ERROR (test,
+                 "DSO should have been a proxy in ns %d,"
+                 " was a non-proxy in ns %d\n",
+                 (int)test->handle_ns, (int)hns);
+          goto cleanup;
+        }
+    }
+ else /* Not expecting a proxy.  */
+    {
+      if (proxy_ns > 0)
+        {
+          ERROR (test,
+                 "DSO should NOT have been a proxy,"
+                 " was a proxy in ns %d (real ns %d)\n",
+                 (int)proxy_ns, (int)hns);
+          goto cleanup;
+        }
+
+      if (test->handle_ns != hns)
+        {
+          ERROR (test,
+                 "DSO should have been in ns %d,"
+                 " was in ns %d\n",
+                 (int)test->handle_ns, (int)hns);
+          goto cleanup;
+        }
+    }
+  TRACE ("proxy status Ok");
+
+  /* Get the new link map contents after the test has run:  */
+  lm_after[0] = link_map_snapshot_array (NULL, process_test_spec,
+                                         LM_ID_BASE, &lm_after_len[0]);
+  for (int i = 1; i < MAX_NS; i++)
+    lm_after[i] = link_map_snapshot_array (NULL, NULL, i, &lm_after_len[i]);
+
+  for (int i = 0; i < MAX_NS; i++)
+    {
+      TRACE("checking status of NS %d", i);
+      void *old_handle =
+        find_test_dso_in_link_map_array (lm_before[i], lm_before_len[i]);
+      TRACE ("old handle is %p", old_handle);
+
+      void *new_handle =
+        find_test_dso_in_link_map_array (lm_after[i], lm_after_len[i]);
+      TRACE ("new handle is %p", new_handle);
+
+      if (test->loaded[i] == NONE)
+        {
+          TRACE ("ns %d requirement is NONE", i);
+          if (old_handle != NULL)
+            {
+              ERROR (test,
+                     "Unexpected preload DSO %s in ns %d\n",
+                     lm_name (old_handle), i);
+              goto cleanup;
+            }
+          if (new_handle != NULL)
+            {
+              ERROR (test, "Unexpected new DSO %s in ns %d\n",
+                     lm_name (new_handle), i);
+              goto cleanup;
+            }
+          continue;
+        }
+
+      if (test->loaded[i] & NEW)
+        {
+          TRACE("ns %d requirement is NEW", i);
+          if (old_handle != NULL)
+            {
+              ERROR (test,
+                     "DSO in ns %d should have been a new load,"
+                     " found to have been preloaded\n", i);
+              goto cleanup;
+            }
+          if (new_handle == NULL)
+            {
+              ERROR (test, "Expected DSO in ns %d, not found\n", i);
+              goto cleanup;
+            }
+        }
+      else
+        {
+          TRACE("ns %d requirement is OLD", i);
+          if (new_handle == NULL)
+            {
+              ERROR (test, "Expected new DSO in ns %d, not found\n", i);
+              goto cleanup;
+            }
+
+          if (old_handle != new_handle)
+            {
+              ERROR (test, "DSO in ns %d changed. This should be impossible, "
+                     "sanity check the test code\n", i);
+              goto cleanup;
+            }
+        }
+
+      if (test->loaded[i] & PROXY)
+        {
+          TRACE ("rechecking DSO status in ns %d", i);
+          if (!((struct link_map *)new_handle)->l_proxy)
+            {
+              ERROR (test, "DSO in ns %d should be a proxy but is not\n", i);
+              goto cleanup;
+            }
+        }
+      else
+        {
+          TRACE ("rechecking proxy status in ns %d", i);
+          if (((struct link_map *)new_handle)->l_proxy)
+            {
+              ERROR (test, "DSO in ns %d should NOT be a proxy but is\n", i);
+              goto cleanup;
+            }
+        }
+    }
+
+  test_status = true;
+
+ cleanup:
+  for (int i = 0; i < MAX_NS; i++)
+     free (lm_after[i]);
+  for (int i = 0; i < MAX_NS; i++)
+    free (lm_before[i]);
+
+  return test_status;
+}
diff --git a/elf/tst-dlmopen-modules.h b/elf/tst-dlmopen-modules.h
new file mode 100644
index 0000000000..63aa7730e3
--- /dev/null
+++ b/elf/tst-dlmopen-modules.h
@@ -0,0 +1,21 @@
+/* Module-specific infrastructure for tst-dlmopen-rtld-*
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#pragma once
+
+#include "tst-dlmopen-common.h"
diff --git a/elf/tst-dlmopen-rtld-audit-shared1.c b/elf/tst-dlmopen-rtld-audit-shared1.c
new file mode 100644
index 0000000000..f61e00d372
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-shared1.c
@@ -0,0 +1,8 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-shared1.h"
+
+#include "tst-dlmopen-std-do-test.c"
+
diff --git a/elf/tst-dlmopen-rtld-audit-shared2.c b/elf/tst-dlmopen-rtld-audit-shared2.c
new file mode 100644
index 0000000000..93d7c9d502
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-shared2.c
@@ -0,0 +1,8 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-shared2.h"
+
+#include "tst-dlmopen-std-do-test.c"
+
diff --git a/elf/tst-dlmopen-rtld-audit-shared3.c b/elf/tst-dlmopen-rtld-audit-shared3.c
new file mode 100644
index 0000000000..bc1e8f0345
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-shared3.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-shared3.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-audit-shared4.c b/elf/tst-dlmopen-rtld-audit-shared4.c
new file mode 100644
index 0000000000..0a0ecd4317
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-shared4.c
@@ -0,0 +1,8 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-shared4.h"
+
+#include "tst-dlmopen-std-do-test.c"
+
diff --git a/elf/tst-dlmopen-rtld-audit-shared5.c b/elf/tst-dlmopen-rtld-audit-shared5.c
new file mode 100644
index 0000000000..1585e48a95
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-shared5.c
@@ -0,0 +1,8 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-shared5.h"
+
+#include "tst-dlmopen-std-do-test.c"
+
diff --git a/elf/tst-dlmopen-rtld-audit-shared6.c b/elf/tst-dlmopen-rtld-audit-shared6.c
new file mode 100644
index 0000000000..f1b21c295b
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-shared6.c
@@ -0,0 +1,8 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-shared6.h"
+
+#include "tst-dlmopen-std-do-test.c"
+
diff --git a/elf/tst-dlmopen-rtld-audit-unique1.c b/elf/tst-dlmopen-rtld-audit-unique1.c
new file mode 100644
index 0000000000..5605967a5c
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-unique1.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-unique1.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-audit-unique2.c b/elf/tst-dlmopen-rtld-audit-unique2.c
new file mode 100644
index 0000000000..8a1d5317c8
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-unique2.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-unique2.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-audit-unique3.c b/elf/tst-dlmopen-rtld-audit-unique3.c
new file mode 100644
index 0000000000..31848ea9d8
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-unique3.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-unique3.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-audit-unique4.c b/elf/tst-dlmopen-rtld-audit-unique4.c
new file mode 100644
index 0000000000..c79cb378d5
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-unique4.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-unique4.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-audit-unique5.c b/elf/tst-dlmopen-rtld-audit-unique5.c
new file mode 100644
index 0000000000..790fed9b04
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-unique5.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-unique5.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-audit-unique6.c b/elf/tst-dlmopen-rtld-audit-unique6.c
new file mode 100644
index 0000000000..0b9bbabdd2
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-audit-unique6.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 2
+#include "tst-dlmopen-rtld-unique6.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-shared1.c b/elf/tst-dlmopen-rtld-shared1.c
new file mode 100644
index 0000000000..329a9a91d9
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared1.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-shared1.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-shared1.h b/elf/tst-dlmopen-rtld-shared1.h
new file mode 100644
index 0000000000..de718fd8ca
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared1.h
@@ -0,0 +1,64 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen:0:none--ns0",
+    .desc = "dlmopen as dlopen",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .loaded = { [0] = DSO|NEW },
+    .handle_type = DSO,
+    .handle_ns = LM_ID_BASE,
+   },
+   {
+    .name = "dlmopen:0:ns0--ns0",
+    .desc = "dlmopen a preloaded DSO in the base NS",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO },
+    .handle_type = DSO,
+    .handle_ns = LM_ID_BASE,
+   },
+   {
+    .name = "dlmopen-shared:0:ns0--ns0",
+    .desc = "dlmopen a preloaded DSO in the base NS with RTLD_SHARED",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO },
+    .handle_type = DSO,
+    .handle_ns = LM_ID_BASE,
+   },
+   {
+    .name = "dlmopen:0:ns0--nsX",
+    .desc = "dlmopen a preloaded DSO in the base NS into a new NS",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_NEWLM,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO|NEW },
+    .handle_type = DSO,
+    .handle_ns = EXPECTED_NS,
+   },
+   {
+    .name = "dlmopen:0:ns0-nsX--ns0-nsX",
+    .desc = "dlmopen a preloaded DSO in the base & secondary NS into the base NS",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .handle_type = DSO,
+    .handle_ns = LM_ID_BASE,
+   },
+   {
+    .name = "dlmopen-shared:0:ns0-nsX--ns0-nsX",
+    .desc = "dlmopen a preloaded DSO in the base & secondary NS into the base NS",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .handle_type = DSO,
+    .handle_ns = LM_ID_BASE,
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-shared2.c b/elf/tst-dlmopen-rtld-shared2.c
new file mode 100644
index 0000000000..b5bfe047e5
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared2.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-shared2.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-shared2.h b/elf/tst-dlmopen-rtld-shared2.h
new file mode 100644
index 0000000000..779ce62dfb
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared2.h
@@ -0,0 +1,66 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-shared:0:none--ns0",
+    .desc = "dlmopen as dlopen with RTLD_SHARED",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .args.flags  = RTLD_SHARED,
+    .loaded = { [0] = DSO|NEW },
+    .handle_ns = LM_ID_BASE,
+    .handle_type = DSO,
+   },
+   {
+    .name = "dlmopen-shared:X:ns0--ns0-nsXp",
+    .desc = "dlmopen into a new namespace with the target already in the base NS",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_NEWLM,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY|NEW }
+   },
+   {
+    .name = "dlmopen:0:ns0-nsXp--ns0-nsXp",
+    .desc = "dlmopen into base NS while proxy already in nsX",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+   {
+    .name = "dlmopen-shared:0:ns0-nsXp--ns0-nsXp",
+    .desc = "dlmopen with RTLD_SHARED into base NS while proxy already in nsX",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+   {
+    .name = "dlmopen:X:ns0-nsXp--ns0-nsXp",
+    .desc = "dlmopen into NS X while proxy already in nsX",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = EXPECTED_NS,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+   {
+    .name = "dlmopen-shared:X:ns0-nsXp--ns0-nsXp",
+    .desc = "dlmopen with RTLD_SHARED into NS X while proxy already in nsX",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = EXPECTED_NS,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-shared3.c b/elf/tst-dlmopen-rtld-shared3.c
new file mode 100644
index 0000000000..f1a4c0fb8a
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared3.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-shared3.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-shared3.h b/elf/tst-dlmopen-rtld-shared3.h
new file mode 100644
index 0000000000..894626b1f4
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared3.h
@@ -0,0 +1,43 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen:X:none--nsX",
+    .desc = "dlmopen into nsX, no copies preloaded",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_NEWLM,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { },
+    .loaded = { [EXPECTED_NS] = DSO|NEW },
+   },
+   {
+    .name = "dlmopen:0:nsX--ns0-nsX",
+    .desc = "dlmopen into ns 0, copy already loaded in ns X",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen:X:ns0-nsX--nsX",
+    .desc = "dlmopen into ns X, copies already in ns 0 and ns X",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = EXPECTED_NS,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-shared:X:ns0-nsX--nsX",
+    .desc = "dlmopen RTLD_SHARED into nsX with a DSO already in NS0 and NSX",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = EXPECTED_NS,
+    .args.flags = RTLD_SHARED,
+    .failure = 1,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-shared4.c b/elf/tst-dlmopen-rtld-shared4.c
new file mode 100644
index 0000000000..395ced83c2
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared4.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-shared4.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-shared4.h b/elf/tst-dlmopen-rtld-shared4.h
new file mode 100644
index 0000000000..9b4d0f06e0
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared4.h
@@ -0,0 +1,14 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-shared:X:none--ns0-nsX",
+    .desc = "dlmopen a new proxy in nsX with no preexisting dso in ns0",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_NEWLM,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { },
+    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = PROXY|NEW },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-shared5.c b/elf/tst-dlmopen-rtld-shared5.c
new file mode 100644
index 0000000000..fe830b4162
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared5.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-shared5.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-shared5.h b/elf/tst-dlmopen-rtld-shared5.h
new file mode 100644
index 0000000000..13dca63cbd
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared5.h
@@ -0,0 +1,25 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-preload:X:none--nsX",
+    .desc = "preload a DSO into ns1 to prepare for other tests",
+    .is_prep_stage = 1,
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_NEWLM,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { },
+    .loaded = { [EXPECTED_NS] = DSO|NEW },
+   },
+   {
+    .name = "dlmopen-shared:0:nsX--nsX-ns0",
+    .desc = "dlmopen RTLD_SHARED into ns0 when preloaded into nsX",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-shared6.c b/elf/tst-dlmopen-rtld-shared6.c
new file mode 100644
index 0000000000..989a37d78a
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared6.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-shared6.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-shared6.h b/elf/tst-dlmopen-rtld-shared6.h
new file mode 100644
index 0000000000..de41333619
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-shared6.h
@@ -0,0 +1,36 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-preload:X:none--nsX",
+    .desc = "preload a DSO into nsX to prepare for other tests",
+    .is_prep_stage = 1,
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = LM_ID_NEWLM,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { },
+    .loaded = { [EXPECTED_NS] = DSO|NEW },
+   },
+   {
+    .name = "dlmopen:X:nsX--nsX",
+    .desc = "dlmopen a dso in nsX while already loaded there",
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = EXPECTED_NS,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { [EXPECTED_NS] = DSO },
+    .loaded = { [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-shared:X:nsX--nsX",
+    .desc = "dlmopen RTLD_SHARED a dso in nsX while already loaded there",
+    .failure = 1,
+    .args.dso_path = DSO_NORMAL,
+    .args.ns = EXPECTED_NS,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { [EXPECTED_NS] = DSO },
+    .loaded = { [EXPECTED_NS] = DSO },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-unique1.c b/elf/tst-dlmopen-rtld-unique1.c
new file mode 100644
index 0000000000..f2240b5cd9
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique1.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-unique1.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-unique1.h b/elf/tst-dlmopen-rtld-unique1.h
new file mode 100644
index 0000000000..4b7390f772
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique1.h
@@ -0,0 +1,86 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-unique:0:none--ns0",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into ns0",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { },
+    .loaded = { [0] = DSO|NEW },
+   },
+   {
+    .name = "dlmopen-unique:0:ns0--ns0",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into ns0 while already present",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO },
+   },
+   {
+    .name = "dlmopen-unique-shared:0:ns0--ns0",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns0 while already present",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO },
+   },
+   {
+    .name = "dlmopen-unique:X:ns0--nsX",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into nsX while present in ns0",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_NEWLM,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY|NEW },
+   },
+   {
+    .name = "dlmopen-unique:0:ns0-nsXp--ns0--nsXp",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso already in ns0 proxied in nsX",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+   {
+    .name = "dlmopen-shared-unique:0:ns0-nsXp--ns0-nsXp",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns0 already in ns0 and proxied in nsX",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+   {
+    .name = "dlmopen-unique:0:ns0-nsXp--ns0-nsXp",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into ns0 already in ns0 and proxied in nsX",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = EXPECTED_NS,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+   {
+    .name = "dlmopen-unique:0:ns0-nsXp--ns0-nsXp",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns0 already in ns0 and proxied in nsX",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = EXPECTED_NS,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-unique2.c b/elf/tst-dlmopen-rtld-unique2.c
new file mode 100644
index 0000000000..5dd8c2678d
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique2.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-unique2.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-unique2.h b/elf/tst-dlmopen-rtld-unique2.h
new file mode 100644
index 0000000000..bf6871ab58
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique2.h
@@ -0,0 +1,25 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-shared-unique:0:none--ns0",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso in the base ns",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { },
+    .loaded = { [0] = DSO|NEW },
+   },
+   {
+    .name = "dlmopen-shared-unique:1:ns0--ns0-ns1p",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns1 while present in ns0",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_NEWLM,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { [0] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY|NEW },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-unique3.c b/elf/tst-dlmopen-rtld-unique3.c
new file mode 100644
index 0000000000..237b3adcc1
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique3.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-unique3.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-unique3.h b/elf/tst-dlmopen-rtld-unique3.h
new file mode 100644
index 0000000000..139ade0d28
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique3.h
@@ -0,0 +1,13 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-unique:X:none--ns0-ns1p",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into nsX",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_NEWLM,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { },
+    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = PROXY|NEW },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-unique4.c b/elf/tst-dlmopen-rtld-unique4.c
new file mode 100644
index 0000000000..e13ca19fc7
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique4.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-unique4.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-unique4.h b/elf/tst-dlmopen-rtld-unique4.h
new file mode 100644
index 0000000000..1a9b93c2ac
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique4.h
@@ -0,0 +1,14 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-shared-unique:X:none--ns0-nsXp",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_NEWLM,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = PROXY,
+    .preloaded = { },
+    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = PROXY|NEW },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-unique5.c b/elf/tst-dlmopen-rtld-unique5.c
new file mode 100644
index 0000000000..014b10e45e
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique5.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-unique5.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-unique5.h b/elf/tst-dlmopen-rtld-unique5.h
new file mode 100644
index 0000000000..75dca65821
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique5.h
@@ -0,0 +1,58 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-isolate-unique:X:none--nsX",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into NSX with RTLD_ISOLATE",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_NEWLM,
+    .args.flags = RTLD_ISOLATE,
+    .handle_ns = EXPECTED_NS,
+    .free_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { },
+    .loaded = { [EXPECTED_NS] = DSO|NEW },
+   },
+   {
+    .name = "dlmopen-unique:0:nsX--ns0-nsX",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso already present in NS X",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-unique:0:ns0-nsX--ns0-nsX",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso already in the base NS and NS X",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-shared-unique:0:ns0-nsX--ns0-nsX",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already in the base NS and NS X",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-shared-unique:X:ns0-nsX--ns0-nsX",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already in the base NS and NS X into NS X",
+    .failure = 1,
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = EXPECTED_NS,
+    .args.flags = RTLD_SHARED,
+    .handle_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+   },
+  };
diff --git a/elf/tst-dlmopen-rtld-unique6.c b/elf/tst-dlmopen-rtld-unique6.c
new file mode 100644
index 0000000000..c0df189d3f
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique6.c
@@ -0,0 +1,7 @@
+#include <dlfcn.h>
+#include "tst-dlmopen-main.h"
+
+#define EXPECTED_NS 1
+#include "tst-dlmopen-rtld-unique6.h"
+
+#include "tst-dlmopen-std-do-test.c"
diff --git a/elf/tst-dlmopen-rtld-unique6.h b/elf/tst-dlmopen-rtld-unique6.h
new file mode 100644
index 0000000000..062f1f2277
--- /dev/null
+++ b/elf/tst-dlmopen-rtld-unique6.h
@@ -0,0 +1,51 @@
+static dlmopen_test_spec dltest[] =
+  {
+   {
+    .name = "dlmopen-isolate-unique:1:none--ns1--prep",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into NS1 with RTLD_ISOLATE",
+    .is_prep_stage = 1,
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_NEWLM,
+    .args.flags = RTLD_ISOLATE,
+    .handle_ns = EXPECTED_NS,
+    .free_ns = EXPECTED_NS,
+    .handle_type = DSO,
+    .preloaded = { },
+    .loaded = { [EXPECTED_NS] = DSO|NEW },
+   },
+   {
+    .name = "dlmopen-unique:1:nsX--nsX--FAIL",
+    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into NSX when already there",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = EXPECTED_NS,
+    .failure = 1,
+    .preloaded = { [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-shared-unique:X:nsX--nsX--FAIL",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into NSX when already there",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = EXPECTED_NS,
+    .args.flags = RTLD_SHARED,
+    .failure = 1,
+    .preloaded = { [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-shared-unique:0:nsX--ns0-nsX",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already present in NS X",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = LM_ID_BASE,
+    .handle_ns = 0,
+    .handle_type = DSO,
+    .preloaded = { [EXPECTED_NS] = DSO },
+    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
+   },
+   {
+    .name = "dlmopen-shared-unique:X:ns0-nsX--ns0-nsX--FAIL",
+    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already in the base NS and NS 1",
+    .args.dso_path = DSO_UNIQUE,
+    .args.ns = EXPECTED_NS,
+    .failure = 1,
+    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
+   },
+  };
diff --git a/elf/tst-dlmopen-sharedmod-norm.c b/elf/tst-dlmopen-sharedmod-norm.c
new file mode 100644
index 0000000000..915a256cd9
--- /dev/null
+++ b/elf/tst-dlmopen-sharedmod-norm.c
@@ -0,0 +1,34 @@
+#include "tst-dlmopen-modules.h"
+#include <signal.h>
+#include <time.h>
+#include <stdlib.h>
+
+static char user_name[255];
+
+/* This initialiser checks that relocation from DF_GNU_1_UNIQUE
+   dependencies (via DT_NEEDED) work properly - it will segfault
+   if _dl_lookup_symbol_x isn't handling that case. The main test
+   framework for dlmopen checks relocations/lookups via dlmopen.  */
+void __attribute__ ((constructor)) check_relocation (void)
+{
+  char *user = getenv("USER");
+
+  if (user == NULL)
+    return;
+
+  for (int i = 0; i < sizeof (user_name) && *(user + i); i++)
+    user_name[i] = *(user + i);
+
+  user[sizeof (user_name) - 1] = 0;
+}
+
+dlmopen_testresult *rtld_shared_testfunc (void)
+{
+  static dlmopen_testresult result;
+
+  result.name = "norm";
+  result.free = free;
+  result.timer_create = timer_create;
+
+  return &result;
+}
diff --git a/elf/tst-dlmopen-sharedmod-uniq.c b/elf/tst-dlmopen-sharedmod-uniq.c
new file mode 100644
index 0000000000..ec646c0d15
--- /dev/null
+++ b/elf/tst-dlmopen-sharedmod-uniq.c
@@ -0,0 +1,33 @@
+#include "tst-dlmopen-modules.h"
+#include <signal.h>
+#include <time.h>
+
+static char user_name[255];
+
+/* This initialiser checks that relocation from DF_GNU_1_UNIQUE
+   dependencies (via DT_NEEDED) work properly - it will segfault
+   if _dl_lookup_symbol_x isn't handling that case. The main test
+   framework for dlmopen checks relocations/lookups via dlmopen.  */
+void __attribute__ ((constructor)) check_relocation (void)
+{
+  char *user = getenv("USER");
+
+  if (user == NULL)
+    return;
+
+  for (int i = 0; i < sizeof (user_name) && *(user + i); i++)
+    user_name[i] = *(user + i);
+
+  user[sizeof (user_name) - 1] = 0;
+}
+
+dlmopen_testresult *rtld_shared_testfunc (void)
+{
+  static dlmopen_testresult result;
+
+  result.name = "noop";
+  result.free = free;
+  result.timer_create = timer_create;
+
+  return &result;
+}
diff --git a/elf/tst-dlmopen-std-do-test.c b/elf/tst-dlmopen-std-do-test.c
new file mode 100644
index 0000000000..9f1d3a3767
--- /dev/null
+++ b/elf/tst-dlmopen-std-do-test.c
@@ -0,0 +1,12 @@
+#include <array_length.h>
+
+static int
+do_test (void)
+{
+  for (int i = 0; i < array_length (dltest); i++)
+    if (!process_test_spec (&dltest[i]))
+      return 1;
+  return 0;
+}
+
+#include <support/test-driver.c>
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 7/8] Restore separate libc loading for the TLS/namespace storage test
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
                   ` (5 preceding siblings ...)
  2021-07-08 16:32 ` [RFC][PATCH v12 6/8] Add dlmopen / RTLD_SHARED tests Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 19:08   ` Adhemerval Zanella via Libc-alpha
  2021-07-08 16:32 ` [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO Vivek Das Mohapatra via Libc-alpha
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

tst-tls-ie-dlmopen checks to see that new namespaces consume
TLS memory as expected: This does not happen when new namespaces
share the same libc instance (since TLS is allocated only when
a new libc instance insitialises its threading infrastructure).

Adding RTLD_ISOLATE to the dlmopen flags in the test restores
the old behaviour which allows the test to check what it
actually needs to.
---
 elf/tst-tls-ie-dlmopen.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/elf/tst-tls-ie-dlmopen.c b/elf/tst-tls-ie-dlmopen.c
index a579d72d2d..e67017db2d 100644
--- a/elf/tst-tls-ie-dlmopen.c
+++ b/elf/tst-tls-ie-dlmopen.c
@@ -53,7 +53,7 @@ static void *
 load_and_access (Lmid_t lmid, const char *mod, const char *func)
 {
   /* Load module with TLS.  */
-  void *p = xdlmopen (lmid, mod, RTLD_NOW);
+  void *p = xdlmopen (lmid, mod, RTLD_NOW|RTLD_ISOLATE);
   /* Access the TLS variable to ensure it is allocated.  */
   void (*f) (void) = (void (*) (void))xdlsym (p, func);
   f ();
@@ -95,7 +95,7 @@ do_test (void)
      than 1024 bytes are available (exact number depends on TLS optimizations
      and the libc TLS use).  */
   printf ("The next dlmopen should fail...\n");
-  void *p = dlmopen (LM_ID_BASE, "tst-tls-ie-mod4.so", RTLD_NOW);
+  void *p = dlmopen (LM_ID_BASE, "tst-tls-ie-mod4.so", RTLD_NOW|RTLD_ISOLATE);
   if (p != NULL)
     FAIL_EXIT1 ("error: expected dlmopen to fail because there is "
 		"not enough surplus static TLS.\n");
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
                   ` (6 preceding siblings ...)
  2021-07-08 16:32 ` [RFC][PATCH v12 7/8] Restore separate libc loading for the TLS/namespace storage test Vivek Das Mohapatra via Libc-alpha
@ 2021-07-08 16:32 ` Vivek Das Mohapatra via Libc-alpha
  2021-08-09 20:10   ` Adhemerval Zanella via Libc-alpha
  2021-08-09 21:24   ` Florian Weimer via Libc-alpha
  2021-07-13 13:08 ` [External] : [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Alfonso Alfonso Peterssen via Libc-alpha
  2021-08-09 20:34 ` Adhemerval Zanella via Libc-alpha
  9 siblings, 2 replies; 24+ messages in thread
From: Vivek Das Mohapatra via Libc-alpha @ 2021-07-08 16:32 UTC (permalink / raw)
  To: libc-alpha

libpthread should have no contents that are sensitive
to duplication anymore, so doesn't require special
treatment under dlmopen.
---
 extra-lib.mk  | 2 ++
 htl/Makefile  | 3 ++-
 nptl/Makefile | 3 ++-
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/extra-lib.mk b/extra-lib.mk
index 9051958ec0..38184adae1 100644
--- a/extra-lib.mk
+++ b/extra-lib.mk
@@ -102,9 +102,11 @@ $(objpfx)$(lib).so: $(firstword $($(lib)-map) \
 					    $(filter $(lib).map, \
 						     $(version-maps))))
 ifneq ($(ld-zunique),yes)
+ifneq ($(lib),libpthread)
 $(objpfx)$(lib).so: $(common-objpfx)/elf/dynamic-notes.os
 endif
 endif
+endif
 
 endif
 
diff --git a/htl/Makefile b/htl/Makefile
index c2a25dcd79..ce6d9a8cd1 100644
--- a/htl/Makefile
+++ b/htl/Makefile
@@ -202,9 +202,10 @@ $(inst_libdir)/libpthread_syms.a: $(srcdir)/libpthread_syms.a $(+force)
 libc-link.so = $(common-objpfx)libc.so
 
 extra-B-pthread.so = -B$(common-objpfx)htl/
+# Turn off DF_GNU_1_UNIQUE for libpthread now that it's a stub.
 LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
 ifeq ($(ld-zunique),yes)
-LDFLAGS-pthread.so += -Wl,-z,unique
+LDFLAGS-pthread.so += -Wl,-z,nounique
 endif
 
 include ../Rules
diff --git a/nptl/Makefile b/nptl/Makefile
index 81353b9455..7fcddbb263 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -376,8 +376,9 @@ CPPFLAGS-tst-pthread-gdb-attach-static.c := \
 tst-pthread-gdb-attach-no-pie = yes
 
 LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
+# Turn off DF_GNU_1_UNIQUE for libpthread now that it's a stub.
 ifeq ($(ld-zunique),yes)
-LDFLAGS-pthread.so += -Wl,-z,unique
+LDFLAGS-pthread.so += -Wl,-z,nounique
 endif
 
 tests += tst-cancelx7 tst-cancelx17 tst-cleanupx4
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [External] : [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
                   ` (7 preceding siblings ...)
  2021-07-08 16:32 ` [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO Vivek Das Mohapatra via Libc-alpha
@ 2021-07-13 13:08 ` Alfonso Alfonso Peterssen via Libc-alpha
  2021-08-09 20:34 ` Adhemerval Zanella via Libc-alpha
  9 siblings, 0 replies; 24+ messages in thread
From: Alfonso Alfonso Peterssen via Libc-alpha @ 2021-07-13 13:08 UTC (permalink / raw)
  To: libc-alpha@sourceware.org, Vivek Das Mohapatra

These patches + https://patchwork.sourceware.org/project/glibc/patch/20200626193228.1953-2-danielwa@cisco.com/ allow to spawn several isolated JVMs within the same process, each one with its own isolated set of native libraries, including the Linux graphical stack, something that wasn't possible before.

The JVM exercises MANY corner cases with its native libraries; we managed to run NetBeans, jEdit, MochaDoom, kotNES... (all graphical applications) inside isolated namespaces, something we've been struggling for years and finally we see some light.
This is a huge milestone, if such complex applications can run, most likely everything else will just work.
We look forward to integrating these patches to make dlmopen fully usable and bug-free.
Best,
Alfonso²


________________________________
From: Libc-alpha <libc-alpha-bounces+alfonso.peterssen=oracle.com@sourceware.org> on behalf of Vivek Das Mohapatra via Libc-alpha <libc-alpha@sourceware.org>
Sent: Thursday, July 8, 2021 6:32 PM
To: libc-alpha@sourceware.org <libc-alpha@sourceware.org>
Subject: [External] : [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen

This is a revision of a previous patchset that I posted here
regarding https://urldefense.com/v3/__https://sourceware.org/bugzilla/show_bug.cgi?id=22745__;!!ACWV5N9M2RV99hQ!eV1V1VkTp5FGE9qvHYkT0J0ksr5o_bFn6aR88pY9tCmVNXczixTZHFkGZa5Fwr0cfbIaEw$

Introduction:

=======================================================================
  As discussed in the URL above dlmopen requires a mechanism for
  [optionally] sharing some objects between more than one namespace.

  The following patchset provides an implementation for this: If an
  object is loaded with the new RTLD_SHARED flag we instead ensure
  that a "master" copy exists (and is flagged as no-delete) in the
  main namespace and a thin wrapper or clone is placed in the target
  namespace.

  This patch series should address all the comments received on the
  earlier (v1-v10) series.

=======================================================================

Changes from v11:

 - If a DSO is required in a non-base namespace because it is mentioned
   in a DT_NEEDED entry and it is itself flagged DF_GNU_1_UNIQUE then
   a proxy is generated for it.

 - Relocations via non-base namespace proxies work reliably (some code
   paths did not do the address calculation relative to the DSO base
   correctly when a proxy was involved).

 - Tests extended cover the above two scenarios.

Changes from v10:

 - A segfault in a dlmopen error pathway (which does not seem to have existed
   when v10 was applied to the then-HEAD commit) has been fixed.

 - The fallback mechanism for adding DT_GNU_FLAGS_1 sections to the required
   binaries has had some infrastructure moved to the elf/ directory

 - The runstatedir setting introduced by recent autoconf has been omitted from
   the patchset as it is not relevant to this feature.

 - libpthread no longer tagged DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE.
   (with both -z unique supporting linkers and if the .os hack is
   used to add the new flag - this was inconsistent before).

Not changed:

 - There is still some diagnostic info in the config.log when the linker
   layout is acceptable but -z unique is not yet supported. I believe this
   _is_ useful diagnostic information as a developer might otherwise wonder
   why the vanilla linker was being rejected when its layout output seemed
   fine.

I have not yet implemented, but plan to address once this series is
accepted/acceptable:

 - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)

 - Check RTLD_GLOBAL interacts properly and unsurprisingly with RTLD_SHARED.

Vivek Das Mohapatra (8):
  Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745)
  Abstract loaded-DSO search code into a helper function
  Use the new DSO finder helper function
  Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745)
  Implement dlmopen RTLD_SHARED flag (bug 22745)
  Add dlmopen / RTLD_SHARED tests
  Restore separate libc loading for the TLS/namespace storage test
  Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO

 Makeconfig                           |   3 +
 Makerules                            |  18 +-
 bits/dlfcn.h                         |  10 +
 config.make.in                       |   1 +
 configure                            |  42 +-
 configure.ac                         |  31 +-
 elf/Makefile                         | 109 +++-
 elf/dl-close.c                       |  43 +-
 elf/dl-deps.c                        |  17 +
 elf/dl-fini.c                        |   6 +-
 elf/dl-init.c                        |   4 +-
 elf/dl-load.c                        | 223 ++++++-
 elf/dl-lookup.c                      |  26 +-
 elf/dl-object.c                      |  78 +++
 elf/dl-open.c                        | 121 +++-
 elf/dl-sym.c                         |  14 +
 elf/dynamic-notes.c                  |   4 +
 elf/elf.h                            |   7 +-
 elf/get-dynamic-info.h               |  12 +
 elf/rtld.c                           |   2 +-
 elf/tst-dlmopen-auditmod.c           |  23 +
 elf/tst-dlmopen-common.h             |  33 +
 elf/tst-dlmopen-main.h               | 879 +++++++++++++++++++++++++++
 elf/tst-dlmopen-modules.h            |  21 +
 elf/tst-dlmopen-rtld-audit-shared1.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared2.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared3.c |   7 +
 elf/tst-dlmopen-rtld-audit-shared4.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared5.c |   8 +
 elf/tst-dlmopen-rtld-audit-shared6.c |   8 +
 elf/tst-dlmopen-rtld-audit-unique1.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique2.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique3.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique4.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique5.c |   7 +
 elf/tst-dlmopen-rtld-audit-unique6.c |   7 +
 elf/tst-dlmopen-rtld-shared1.c       |   7 +
 elf/tst-dlmopen-rtld-shared1.h       |  64 ++
 elf/tst-dlmopen-rtld-shared2.c       |   7 +
 elf/tst-dlmopen-rtld-shared2.h       |  66 ++
 elf/tst-dlmopen-rtld-shared3.c       |   7 +
 elf/tst-dlmopen-rtld-shared3.h       |  43 ++
 elf/tst-dlmopen-rtld-shared4.c       |   7 +
 elf/tst-dlmopen-rtld-shared4.h       |  14 +
 elf/tst-dlmopen-rtld-shared5.c       |   7 +
 elf/tst-dlmopen-rtld-shared5.h       |  25 +
 elf/tst-dlmopen-rtld-shared6.c       |   7 +
 elf/tst-dlmopen-rtld-shared6.h       |  36 ++
 elf/tst-dlmopen-rtld-unique1.c       |   7 +
 elf/tst-dlmopen-rtld-unique1.h       |  86 +++
 elf/tst-dlmopen-rtld-unique2.c       |   7 +
 elf/tst-dlmopen-rtld-unique2.h       |  25 +
 elf/tst-dlmopen-rtld-unique3.c       |   7 +
 elf/tst-dlmopen-rtld-unique3.h       |  13 +
 elf/tst-dlmopen-rtld-unique4.c       |   7 +
 elf/tst-dlmopen-rtld-unique4.h       |  14 +
 elf/tst-dlmopen-rtld-unique5.c       |   7 +
 elf/tst-dlmopen-rtld-unique5.h       |  58 ++
 elf/tst-dlmopen-rtld-unique6.c       |   7 +
 elf/tst-dlmopen-rtld-unique6.h       |  51 ++
 elf/tst-dlmopen-sharedmod-norm.c     |  34 ++
 elf/tst-dlmopen-sharedmod-uniq.c     |  33 +
 elf/tst-dlmopen-std-do-test.c        |  12 +
 elf/tst-tls-ie-dlmopen.c             |   4 +-
 extra-lib.mk                         |   5 +
 htl/Makefile                         |   4 +
 iconvdata/Makefile                   |   3 +
 iconvdata/extra-module.mk            |   4 +
 include/elf.h                        |   2 +
 include/link.h                       |   7 +-
 nptl/Makefile                        |   8 +-
 sysdeps/generic/ldsodefs.h           |  11 +
 sysdeps/mips/bits/dlfcn.h            |  10 +
 73 files changed, 2455 insertions(+), 77 deletions(-)
 create mode 100644 elf/dynamic-notes.c
 create mode 100644 elf/tst-dlmopen-auditmod.c
 create mode 100644 elf/tst-dlmopen-common.h
 create mode 100644 elf/tst-dlmopen-main.h
 create mode 100644 elf/tst-dlmopen-modules.h
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared1.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared2.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared3.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared4.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared5.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-shared6.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique1.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique2.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique3.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique4.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique5.c
 create mode 100644 elf/tst-dlmopen-rtld-audit-unique6.c
 create mode 100644 elf/tst-dlmopen-rtld-shared1.c
 create mode 100644 elf/tst-dlmopen-rtld-shared1.h
 create mode 100644 elf/tst-dlmopen-rtld-shared2.c
 create mode 100644 elf/tst-dlmopen-rtld-shared2.h
 create mode 100644 elf/tst-dlmopen-rtld-shared3.c
 create mode 100644 elf/tst-dlmopen-rtld-shared3.h
 create mode 100644 elf/tst-dlmopen-rtld-shared4.c
 create mode 100644 elf/tst-dlmopen-rtld-shared4.h
 create mode 100644 elf/tst-dlmopen-rtld-shared5.c
 create mode 100644 elf/tst-dlmopen-rtld-shared5.h
 create mode 100644 elf/tst-dlmopen-rtld-shared6.c
 create mode 100644 elf/tst-dlmopen-rtld-shared6.h
 create mode 100644 elf/tst-dlmopen-rtld-unique1.c
 create mode 100644 elf/tst-dlmopen-rtld-unique1.h
 create mode 100644 elf/tst-dlmopen-rtld-unique2.c
 create mode 100644 elf/tst-dlmopen-rtld-unique2.h
 create mode 100644 elf/tst-dlmopen-rtld-unique3.c
 create mode 100644 elf/tst-dlmopen-rtld-unique3.h
 create mode 100644 elf/tst-dlmopen-rtld-unique4.c
 create mode 100644 elf/tst-dlmopen-rtld-unique4.h
 create mode 100644 elf/tst-dlmopen-rtld-unique5.c
 create mode 100644 elf/tst-dlmopen-rtld-unique5.h
 create mode 100644 elf/tst-dlmopen-rtld-unique6.c
 create mode 100644 elf/tst-dlmopen-rtld-unique6.h
 create mode 100644 elf/tst-dlmopen-sharedmod-norm.c
 create mode 100644 elf/tst-dlmopen-sharedmod-uniq.c
 create mode 100644 elf/tst-dlmopen-std-do-test.c

--
2.20.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 1/8] Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745)
  2021-07-08 16:32 ` [RFC][PATCH v12 1/8] Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745) Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 12:27   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 12:27 UTC (permalink / raw)
  To: libc-alpha, Vivek Das Mohapatra



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> Define a new flags section DT_GNU_FLAGS_1 (no more bits are available
> in DT_GNU_FLAGS).
> 
> One flag is currently defined: DF_GNU_1_UNIQUE.
> 
> libc and its companion DSOs (libpthread et al) should have this
> section and flag set.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  elf/elf.h              |  7 ++++++-
>  elf/get-dynamic-info.h | 12 ++++++++++++
>  include/elf.h          |  2 ++
>  include/link.h         |  1 +
>  4 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/elf/elf.h b/elf/elf.h
> index 2a62b98d4a..448c275d10 100644
> --- a/elf/elf.h
> +++ b/elf/elf.h
> @@ -896,6 +896,7 @@ typedef struct
>     Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's
>     approach.  */
>  #define DT_VALRNGLO	0x6ffffd00
> +#define DT_GNU_FLAGS_1   0x6ffffdf4	/* GNU extension flags */
>  #define DT_GNU_PRELINKED 0x6ffffdf5	/* Prelinking timestamp */
>  #define DT_GNU_CONFLICTSZ 0x6ffffdf6	/* Size of conflict section */
>  #define DT_GNU_LIBLISTSZ 0x6ffffdf7	/* Size of library list */
> @@ -910,7 +911,7 @@ typedef structselectable
>  #define DT_SYMINENT	0x6ffffdff	/* Entry size of syminfo */
>  #define DT_VALRNGHI	0x6ffffdff
>  #define DT_VALTAGIDX(tag)	(DT_VALRNGHI - (tag))	/* Reverse order! */
> -#define DT_VALNUM 12
> +#define DT_VALNUM 13
>  
>  /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
>     Dyn.d_un.d_ptr field of the Elf*_Dyn structure.

Ok.

> @@ -999,6 +1000,10 @@ typedef struct
>  #define	DF_1_WEAKFILTER 0x20000000
>  #define	DF_1_NOCOMMON   0x40000000
>  
> +/* State flags selectable in the `d_un.d_val' element of the DT_GNU_FLAGS_1
> +   entry in the dynamic section.  */
> +#define DF_GNU_1_UNIQUE 0x00000001	/* Load max 1 copy of this DSO.  */
> +
>  /* Flags for the feature selection in DT_FEATURE_1.  */
>  #define DTF_1_PARINIT	0x00000001
>  #define DTF_1_CONFEXP	0x00000002

Ok.

> diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
> index d8ec32377d..8082d84ba1 100644
> --- a/elf/get-dynamic-info.h
> +++ b/elf/get-dynamic-info.h
> @@ -175,6 +175,18 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
>        if (l->l_flags_1 & DF_1_NOW)
>  	info[DT_BIND_NOW] = info[VERSYMIDX (DT_FLAGS_1)];
>      }
> +  if (info[DT_VALTAGIDX (DT_GNU_FLAGS_1)
> +           + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM] != NULL)
> +    {
> +      l->l_gnu_flags_1 = info[DT_VALTAGIDX (DT_GNU_FLAGS_1)
> +                              + DT_NUM + DT_THISPROCNUM
> +                              + DT_VERSIONTAGNUM + DT_EXTRANUM]->d_un.d_val;
> +
> +      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)
> +	  && l->l_gnu_flags_1 & ~DT_GNU_1_SUPPORTED_MASK)
> +	_dl_debug_printf ("\nWARNING: Unsupported flag value(s) of 0x%x in DT_GNU_FLAGS_1.\n",
> +			  l->l_gnu_flags_1 & ~DT_GNU_1_SUPPORTED_MASK);
> +    }
>    if (info[DT_RUNPATH] != NULL)
>      /* If both RUNPATH and RPATH are given, the latter is ignored.  */
>      info[DT_RPATH] = NULL;

Ok.

> diff --git a/include/elf.h b/include/elf.h
> index 14ed67ff67..5eee37c294 100644
> --- a/include/elf.h
> +++ b/include/elf.h
> @@ -25,5 +25,7 @@
>     (DF_1_NOW | DF_1_NODELETE | DF_1_INITFIRST | DF_1_NOOPEN \
>      | DF_1_ORIGIN | DF_1_NODEFLIB | DF_1_PIE)
>  
> +#define DT_GNU_1_SUPPORTED_MASK DF_GNU_1_UNIQUE
> +
>  #endif /* !_ISOMAC */
>  #endif /* elf.h */

Ok.

> diff --git a/include/link.h b/include/link.h
> index 4af16cb596..6ed35eb808 100644
> --- a/include/link.h
> +++ b/include/link.h
> @@ -283,6 +283,7 @@ struct link_map
>      unsigned int l_used;
>  
>      /* Various flag words.  */
> +    ElfW(Word) l_gnu_flags_1;
>      ElfW(Word) l_feature_1;
>      ElfW(Word) l_flags_1;
>      ElfW(Word) l_flags;
> 

OK.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 3/8] Use the new DSO finder helper function
  2021-07-08 16:32 ` [RFC][PATCH v12 3/8] Use the new DSO finder " Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 12:59   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 12:59 UTC (permalink / raw)
  To: libc-alpha

I think it would be better to merge it with the previous patch.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> ---
>  elf/dl-load.c | 28 +++-------------------------
>  1 file changed, 3 insertions(+), 25 deletions(-)
> 
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 26680b7f68..050c64135a 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -2082,32 +2082,10 @@ _dl_map_object (struct link_map *loader, const char *name,
>    assert (nsid < GL(dl_nns));
>  
>    /* Look for this name among those already loaded.  */
> -  for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
> +  l = _dl_find_dso (name, nsid);
> +
> +  if (l != NULL)
>      {
> -      /* If the requested name matches the soname of a loaded object,
> -	 use that object.  Elide this check for names that have not
> -	 yet been opened.  */
> -      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0))
> -	continue;
> -      if (!_dl_name_match_p (name, l))
> -	{
> -	  const char *soname;
> -
> -	  if (__glibc_likely (l->l_soname_added)
> -	      || l->l_info[DT_SONAME] == NULL)
> -	    continue;
> -
> -	  soname = ((const char *) D_PTR (l, l_info[DT_STRTAB])
> -		    + l->l_info[DT_SONAME]->d_un.d_val);
> -	  if (strcmp (name, soname) != 0)
> -	    continue;
> -
> -	  /* We have a match on a new name -- cache it.  */
> -	  add_name_to_object (l, soname);
> -	  l->l_soname_added = 1;
> -	}
> -
> -      /* We have a match.  */
>        return l;
>      }
>  
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 2/8] Abstract loaded-DSO search code into a helper function
  2021-07-08 16:32 ` [RFC][PATCH v12 2/8] Abstract loaded-DSO search code into a helper function Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 13:04   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 13:04 UTC (permalink / raw)
  To: Vivek Das Mohapatra, libc-alpha

LGTM, but I think it should be merge it with next patch.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> ---
>  elf/dl-load.c              | 38 ++++++++++++++++++++++++++++++++++++++
>  sysdeps/generic/ldsodefs.h |  4 ++++
>  2 files changed, 42 insertions(+)
> 
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index a08df001af..26680b7f68 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -2027,6 +2027,44 @@ open_path (const char *name, size_t namelen, int mode,
>    return -1;
>  }
>  
> +/* Search for a shared object in a given namespace.  */
> +struct link_map *
> +_dl_find_dso (const char *name, Lmid_t nsid)
> +{
> +  struct link_map *l;
> +
> +  for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
> +    {
> +      /* If the requested name matches the soname of a loaded object,
> +	 use that object.  Elide this check for names that have not
> +	 yet been opened.  */
> +      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0))
> +	continue;
> +      if (!_dl_name_match_p (name, l))
> +	{
> +	  const char *soname;
> +
> +	  if (__glibc_likely (l->l_soname_added)
> +	      || l->l_info[DT_SONAME] == NULL)
> +	    continue;
> +
> +	  soname = ((const char *) D_PTR (l, l_info[DT_STRTAB])
> +		    + l->l_info[DT_SONAME]->d_un.d_val);
> +	  if (strcmp (name, soname) != 0)
> +	    continue;
> +
> +	  /* We have a match on a new name -- cache it.  */
> +	  add_name_to_object (l, soname);
> +	  l->l_soname_added = 1;
> +	}
> +
> +      /* We have a match.  */
> +      return l;
> +    }
> +
> +  return NULL;
> +}
> +
>  /* Map in the shared object file NAME.  */
>  
>  struct link_map *
> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 176394de4d..44e7097712 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1287,6 +1287,10 @@ extern void _dl_show_scope (struct link_map *new, int from)
>  extern struct link_map *_dl_find_dso_for_object (const ElfW(Addr) addr);
>  rtld_hidden_proto (_dl_find_dso_for_object)
>  
> +extern struct link_map *_dl_find_dso (const char *name, Lmid_t nsid);
> +rtld_hidden_proto (_dl_find_dso)
> +
> +
>  /* Initialization which is normally done by the dynamic linker.  */
>  extern void _dl_non_dynamic_init (void)
>       attribute_hidden;
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745)
  2021-07-08 16:32 ` [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745) Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 16:48   ` Adhemerval Zanella via Libc-alpha
  2021-08-10 13:21     ` Adhemerval Zanella via Libc-alpha
  0 siblings, 1 reply; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 16:48 UTC (permalink / raw)
  To: libc-alpha, Vivek Das Mohapatra



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> libc.so, libpthread.so etc should have the new unique-dso-by-default
> flag set to allow dlmopen to work better (libc et al instance shared
> by default when DSOs dlmopened into a new namespace).

The patch looks ok, some comments below.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>


> ---
>  Makeconfig                |  3 +++
>  Makerules                 | 18 ++++++++++++++++-
>  config.make.in            |  1 +
>  configure                 | 42 ++++++++++++++++++++++++++++++++++++---
>  configure.ac              | 31 ++++++++++++++++++++++++++---
>  elf/Makefile              |  3 +++
>  elf/dynamic-notes.c       |  4 ++++
>  extra-lib.mk              |  3 +++
>  htl/Makefile              |  3 +++
>  iconvdata/Makefile        |  3 +++
>  iconvdata/extra-module.mk |  4 ++++
>  nptl/Makefile             |  7 ++++++-
>  12 files changed, 114 insertions(+), 8 deletions(-)
>  create mode 100644 elf/dynamic-notes.c
> 
> diff --git a/Makeconfig b/Makeconfig
> index efc7351d71..baa893840e 100644
> --- a/Makeconfig
> +++ b/Makeconfig
> @@ -398,6 +398,9 @@ LDFLAGS-lib.so += -Wl,-z,now
>  # Extra flags for dynamically linked non-test main programs.
>  link-extra-flags += -Wl,-z,now
>  endif
> +ifeq ($(ld-zunique),yes)
> +LDFLAGS-lib.so += -Wl,-z,unique
> +endif
>  
>  # Command to run after every final link (executable or shared object).
>  # This is invoked with $(call after-link,...), so it should operate on

Ok.

> diff --git a/Makerules b/Makerules
> index 596fa68376..66c1251754 100644
> --- a/Makerules
> +++ b/Makerules
> @@ -581,7 +581,8 @@ $(common-objpfx)shlib.lds: $(common-objpfx)config.make $(..)Makerules
>  		 PROVIDE(__start___libc_IO_vtables = .);\
>  		 __libc_IO_vtables : { *(__libc_IO_vtables) }\
>  		 PROVIDE(__stop___libc_IO_vtables = .);\
> -		 /DISCARD/ : { *(.gnu.glibc-stub.*) }@'
> +		 /DISCARD/ : { *(.gnu.glibc-stub.*) }@' \
> +	      -e 's/^.*\*(\.dynamic).*$$/   .dynamic : { *dynamic-notes.os(.dynamic) *(.dynamic) }/'
>  	test -s $@T
>  	mv -f $@T $@
>  common-generated += shlib.lds

Ok.

> @@ -636,6 +637,16 @@ build-shlib-objlist = $(build-module-helper-objlist) \
>  # Also omits crti.o and crtn.o, which we do not want
>  # since we define our own `.init' section specially.
>  LDFLAGS-c.so = -nostdlib -nostartfiles
> +
> +# The stub dynamic-notes.os should not have weak/undefined symbol magic in it.
> +# It's not really part of the internals, rather it is a vector for linker magic
> +# which we need when the linker isn't new enough:
> +%/dynamic-notes.os: CPPFLAGS += -UMODULE_NAME
> +
> +ifeq ($(ld-zunique),yes)
> +LDFLAGS-c.so += -Wl,-z,unique
> +endif
> +
>  # But we still want to link libc.so against $(libc.so-gnulib).
>  LDLIBS-c.so += $(libc.so-gnulib)
>  # Give libc.so an entry point and make it directly runnable itself.

Ok.

> @@ -706,6 +717,11 @@ $(common-objpfx)linkobj/libc.so: $(common-objpfx)linkobj/libc_pic.a \
>  	$(build-shlib)
>  	$(call after-link,$@)
>  
> +ifneq ($(ld-zunique),yes)
> +$(common-objpfx)libc.so: $(common-objpfx)elf/dynamic-notes.os
> +$(common-objpfx)linkobj/libc.so: $(common-objpfx)elf/dynamic-notes.os
> +endif
> +
>  ifeq ($(build-shared),yes)
>  $(common-objpfx)libc.so: $(common-objpfx)libc.map
>  endif

OK.

> diff --git a/config.make.in b/config.make.in
> index cbf59114b0..8755490c13 100644
> --- a/config.make.in
> +++ b/config.make.in
> @@ -72,6 +72,7 @@ have-cc-with-libunwind = @libc_cv_cc_with_libunwind@
>  fno-unit-at-a-time = @fno_unit_at_a_time@
>  bind-now = @bindnow@
>  have-hash-style = @libc_cv_hashstyle@
> +ld-zunique = @ld_zunique@
>  use-default-link = @use_default_link@
>  have-cxx-thread_local = @libc_cv_cxx_thread_local@
>  have-loop-to-function = @libc_cv_cc_loop_to_function@

Ok.

> diff --git a/configure b/configure
> index 9619c10991..a53243a178 100755
> --- a/configure
> +++ b/configure
> @@ -625,6 +625,7 @@ libc_cv_cc_nofma
>  libc_cv_mtls_dialect_gnu2
>  fno_unit_at_a_time
>  libc_cv_has_glob_dat
> +ld_zunique
>  libc_cv_hashstyle
>  libc_cv_fpie
>  libc_cv_z_execstack
> @@ -6074,9 +6075,38 @@ fi
>  $as_echo "$libc_cv_hashstyle" >&6; }
>  
>  
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support" >&5
> +$as_echo_n "checking for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support... " >&6; }
> +if ${libc_cv_ld_zunique+:} false; then :
> +  $as_echo_n "(cached) " >&6
> +else
> +  cat > conftest.ld.c <<\EOF
> +int x (void) { return 0; }
> +EOF
> +
> +libc_cv_ld_zunique=no;
> +
> +if { ac_try='${CC-cc} -Wl,--fatal-warnings -Wl,-z,unique -shared -o conftest.ld.so conftest.ld.c'
> +  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
> +  (eval $ac_try) 2>&5
> +  ac_status=$?
> +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> +  test $ac_status = 0; }; }
> +then
> +  libc_cv_ld_zunique=yes
> +fi;
> +ld_zunique=$libc_cv_ld_zunique
> +rm -f conftest*
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_ld_zunique" >&5
> +$as_echo "$libc_cv_ld_zunique" >&6; }
> +
> +
>  # The linker's default -shared behavior is good enough if it
>  # does these things that our custom linker scripts ensure that
> -# all allocated NOTE sections come first.
> +# all allocated NOTE sections come first AND it understands
> +# -z unique should result in DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE
> +# sections in its output.
>  if test "$use_default_link" = default; then
>    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sufficient default -shared layout" >&5
>  $as_echo_n "checking for sufficient default -shared layout... " >&6; }
> @@ -6119,10 +6149,16 @@ EOF
>        esac
>        shift 2
>      done
> -    case "$libc_seen_a$libc_seen_b" in
> -    yesyes)
> +    case "$libc_seen_a$libc_seen_b$libc_cv_ld_zunique" in
> +    yesyesyes)
>        libc_cv_use_default_link=yes
>        ;;
> +    yesyesno)
> +      echo >&5 "\
> +shared layout from:
> +$ac_try
> +is OK but -Wl,-z,unique is unsupported so linker scripts are required"
> +      ;;
>      *)
>        echo >&5 "\
>  $libc_seen_a$libc_seen_b from:

Ok.

> diff --git a/configure.ac b/configure.ac
> index 34ecbba540..9369bcbebe 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1372,9 +1372,28 @@ fi
>  rm -f conftest*])
>  AC_SUBST(libc_cv_hashstyle)
>  
> +AC_CACHE_CHECK([for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support],

Maybe a space before ' /'.

> +                libc_cv_ld_zunique, [dnl
> +cat > conftest.ld.c <<\EOF
> +int x (void) { return 0; }
> +EOF
> +
> +libc_cv_ld_zunique=no;
> +
> +if AC_TRY_COMMAND([dnl
> +${CC-cc} -Wl,--fatal-warnings -Wl,-z,unique -shared -o conftest.ld.so conftest.ld.c])
> +then
> +  libc_cv_ld_zunique=yes
> +fi;
> +ld_zunique=$libc_cv_ld_zunique
> +rm -f conftest*])
> +AC_SUBST(ld_zunique)
> +

I think we can simplify it with:

  old_LDFLAGS="$LDFLAGS"
  LDFLAGS="-Wl,--fatal-warnings -Wl,-z,unique -shared"
  AC_CACHE_CHECK([for linker DT_GNU_FLAGS_1 / DF_GNU_1_UNIQUE support],
                 AC_LINK_IFELSE([AC_LANG_SOURCE([void foo (void) { }])],
                                libc_cv_ld_zunique=yes,
                                libc_cv_ld_zunique=no))
  LDFLAGS="$old_LDFLAGS"
  AC_SUBST(ld_zunique)


>  # The linker's default -shared behavior is good enough if it
>  # does these things that our custom linker scripts ensure that
> -# all allocated NOTE sections come first.
> +# all allocated NOTE sections come first AND it understands
> +# -z unique should result in DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE
> +# sections in its output.
>  if test "$use_default_link" = default; then
>    AC_CACHE_CHECK([for sufficient default -shared layout],
>  		  libc_cv_use_default_link, [dnl

Ok.

> @@ -1410,10 +1429,16 @@ EOF
>        esac
>        shift 2
>      done
> -    case "$libc_seen_a$libc_seen_b" in
> -    yesyes)
> +    case "$libc_seen_a$libc_seen_b$libc_cv_ld_zunique" in
> +    yesyesyes)
>        libc_cv_use_default_link=yes
>        ;;
> +    yesyesno)
> +      echo >&AS_MESSAGE_LOG_FD "\
> +shared layout from:
> +$ac_try
> +is OK but -Wl,-z,unique is unsupported so linker scripts are required"
> +      ;;
>      *)
>        echo >&AS_MESSAGE_LOG_FD "\
>  $libc_seen_a$libc_seen_b from:

Ok.

> diff --git a/elf/Makefile b/elf/Makefile
> index 698a6ab985..9734030c23 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -103,6 +103,9 @@ endif
>  
>  ifeq (yes,$(build-shared))
>  extra-objs	= $(all-rtld-routines:%=%.os) sofini.os interp.os
> +ifneq (yes,$(ld-zunique))
> +extra-objs      += dynamic-notes.os
> +endif
>  generated	+= librtld.os dl-allobjs.os ld.so ldd
>  install-others	= $(inst_rtlddir)/$(rtld-installed-name)
>  install-bin-script = ldd

Ok.

> diff --git a/elf/dynamic-notes.c b/elf/dynamic-notes.c
> new file mode 100644
> index 0000000000..d0b487e970
> --- /dev/null
> +++ b/elf/dynamic-notes.c
> @@ -0,0 +1,4 @@
> +#include <link.h>
> +
> +const ElfW(Dyn) __dynamic_note __attribute__ ((section (".dynamic"))) =
> +  { .d_tag = DT_GNU_FLAGS_1, .d_un.d_val = DF_GNU_1_UNIQUE };

Ok.

> diff --git a/extra-lib.mk b/extra-lib.mk
> index 72f8d2e1df..9051958ec0 100644
> --- a/extra-lib.mk
> +++ b/extra-lib.mk
> @@ -101,6 +101,9 @@ $(objpfx)$(lib).so: $(firstword $($(lib)-map) \
>  				$(addprefix $(common-objpfx), \
>  					    $(filter $(lib).map, \
>  						     $(version-maps))))
> +ifneq ($(ld-zunique),yes)
> +$(objpfx)$(lib).so: $(common-objpfx)/elf/dynamic-notes.os
> +endif
>  endif
>  
>  endif

Ok.

> diff --git a/htl/Makefile b/htl/Makefile
> index cf9d12fc12..c2a25dcd79 100644
> --- a/htl/Makefile
> +++ b/htl/Makefile
> @@ -203,6 +203,9 @@ libc-link.so = $(common-objpfx)libc.so
>  
>  extra-B-pthread.so = -B$(common-objpfx)htl/
>  LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
> +ifeq ($(ld-zunique),yes)
> +LDFLAGS-pthread.so += -Wl,-z,unique
> +endif
>  
>  include ../Rules
>  

Ok.

> diff --git a/iconvdata/Makefile b/iconvdata/Makefile
> index bb3f621b49..4170005322 100644
> --- a/iconvdata/Makefile
> +++ b/iconvdata/Makefile
> @@ -67,6 +67,9 @@ modules	:= ISO8859-1 ISO8859-2 ISO8859-3 ISO8859-4 ISO8859-5		 \
>  ifeq ($(bind-now),yes)
>  LDFLAGS.so += -Wl,-z,now
>  endif
> +ifeq ($(ld-zunique),yes)
> +LDFLAGS.so += -Wl,-z,unique
> +endif
>  
>  modules.so := $(addsuffix .so, $(modules))
>  

Ok.

> diff --git a/iconvdata/extra-module.mk b/iconvdata/extra-module.mk
> index ecaf507624..92c3ab4d7b 100644
> --- a/iconvdata/extra-module.mk
> +++ b/iconvdata/extra-module.mk
> @@ -7,6 +7,10 @@ $(objpfx)$(mod).so: $(addprefix $(objpfx),$(addsuffix .os,$($(mod)-routines)))\
>  		    $(shlib-lds) $(link-libc-deps)
>  	$(build-module-asneeded)
>  
> +ifneq ($(ld-zunique),yes)
> +$(objpfx)$(mod).so: $(common-objpfx)/elf/dynamic-notes.os
> +endif
> +
>  ifneq (,$(extra-modules-left))
>  include extra-module.mk
>  endif

Ok.

> diff --git a/nptl/Makefile b/nptl/Makefile
> index 9b94bfcd31..81353b9455 100644
> --- a/nptl/Makefile
> +++ b/nptl/Makefile
> @@ -375,7 +375,12 @@ CPPFLAGS-tst-pthread-gdb-attach-static.c := \
>  # were launched with an explicit ld.so invocation.
>  tst-pthread-gdb-attach-no-pie = yes
>  
> -	
> +LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
> +ifeq ($(ld-zunique),yes)
> +LDFLAGS-pthread.so += -Wl,-z,unique
> +endif
> +


I think now that we move all symbols to libpthread, there is no need to mark it
as -z,unique (as you do on a subsequent patch in this set).

> +tests += tst-cancelx7 tst-cancelx17 tst-cleanupx4

I think the extra tst-cleanupx4 is a rebase mistake here.

>  
>  ifeq ($(build-shared),yes)
>  tests += tst-compat-forwarder tst-audit-threads
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag (bug 22745)
  2021-07-08 16:32 ` [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag " Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 19:07   ` Adhemerval Zanella via Libc-alpha
  2021-08-09 20:24     ` Adhemerval Zanella via Libc-alpha
  2021-08-09 20:30   ` Adhemerval Zanella via Libc-alpha
  1 sibling, 1 reply; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 19:07 UTC (permalink / raw)
  To: Vivek Das Mohapatra, libc-alpha



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> This flag will instruct dlmopen to create a shared object present
> in the main namespace and accessible from the selected namespace
> when supplied in the MODE argument.
> 
> include/link.h: Update the link_map struct to allow proxies
> 
> We already have an l_real pointer, used for a similar purpose by
> the linker for copies of ld.so in secondary namespaces. Update its
> documentation and add a bitfield to indicate when link_map entry
> is a proxy.
> 
> elf/dl-object.c: Implement a helper function to proxy link_map entries
> 
> Provides the minimal functionality needed to take an existing
> link_map entry and create a proxy for it in the specified namespace.
> 
> elf/dl-load.c, elf/dl-open.c: Implement RTLD_SHARED dlmopen proxying
> 
> This uses the new infrastructure to implement RTLD_SHARED object
> proxying via dlmopen: Instead of opening the specified object in
> the requested namespace we open it in the main namespace (if it
> is not already present there) and proxy it to the destination.
> 
> The following rules apply:
> 
> If a proxy of the object is already present in the requested namespace,
> we simply return it (with an incremented direct-open count).
> 
> If the object is already present in the requested namespace, a dl
> error is signalled, since we cannot satisfy the user's request.
> 
> Proxies are never created in the main namespace: RTLD_SHARED has no
> effect when the requested namespace is LM_ID_BASE.
> 
> elf/dl-fini.c: Handle proxy link_maps in the shutdown path (bug 22745)
> 
> When cleaning up before exit we should not call destructors or
> otherwise free [most of] the contents of proxied link_map entries
> since they share [most of] their contents with the LM_ID_BASE
> objects to which they point.
> 
> elf/dl-init.c: Skip proxy link_map entries in dl init path
> 
> Proxies should not trigger calls to DT_INIT constructors since they're
> just shims that point to the real, already loaded and initialised, objects.
> 
> elf/dl-open.c: Skip libc init if namespace has no libc map
> 
> Secondary namespaces which share their libc mapping with the main
> namespace cannot (and should not) have _dl_call_libc_early_init
> called for them by dl_open_worker.
> 
> elf/dl-open.c: When creating a proxy check NS 0 libc map
> 
> The libc_already_loaded check normally considers the libc_map entry
> in GL(dl_ns)[args->nsid].libc_map.
> 
> This is not correct for proxies, which use the libc_map from
> the default namespace (as proxies are dummy entries that point
> to the base namespace via their l_real members).
> 
> elf/dl-load.c, dl-open.c: Compare DSOs by file ID & check DF_GNU_1_UNIQUE
> 
> If _dl_map_object_from_fd finds that a DSO it was asked to
> load into a non-base namespace is already loaded (into the
> main namespace) and is flagged DF_GNU_1_UNIQUE then it should
> return that DSO's link map entry.
> 
> In such cases _dl_open_worker must notice that this has
> happened and continue down the link map proxy generation
> path instead of normal link map entry preparation.
> 
> elf/dl-load.c: error if RTLD_SHARED = DF_GNU_1_UNIQUE semantics violated
> 
> elf/dl-open.c: Use search helper to find preloaded DT_GNU_UNIQUE DSOs
> 
> If a DSO already exists (with the same name) in the base namespace
> and it is flagged DT_GNU_UNIQUE then we should behave as if a proxy
> had been requested.
> 
> elf/dl-load.c: When loading DSOs in other namespaces check DT_GNU_UNIQUE
> 
> If a DSO has not already been loaded and the target is not the main
> namespace then we must check to see if it's been DT_GNU_UNIQUE tagged
> and load it into the main namespace instead.
> 
> dl_open_worker has alread been modified to notice the discrepancy
> between the request and the result in such cases, and will set up
> a proxy in the target namespace.
> 
> elf/dl-load.c: Suppress audit calls when a (new) namespace is empty
> 
> When preparing an RTLD_SHARED proxy in a new namespace
> it is possible for the target namespace to be empty:
> 
> This can happen for RTLD_SHARED + LM_ID_NEWLM.
> 
> The audit infrastructure should not be invoked at this
> point (as there's nothing there to audit yet).
> 
> bits/dlfcn.h, elf/dl-load.c, elf/dl-open.c, elf/rtld.c,
> sysdeps/mips/bits/dlfcn.h:
> Suppress inter-namespace DSO sharing for audit libraries
> 
> Audit libraries should not participate in DSO sharing: In
> particular libraries tagged with DF_GNU_1_UNIQUE should not
> be shared between the audit namespace and any others - they
> should get their own copy.
> 
> This is signalled to the loader code by passing the RTLD_ISOLATE
> flag from the relevant entry point in the dl modes argument.
> 
> elf/dl-sym.c: dlsym, dlvsym must be able to resolve symbols via proxies

Patch look good in general, but I have some comments and improvements below.
The main change is to remove the 'has_gnu_unique' function and move the code
on 'open_verify()'. 

There are also multiple indentation mismatches, where the used one differs 
from what the current files uses.

> ---
>  bits/dlfcn.h               |  10 ++
>  elf/dl-close.c             |  43 +++++----
>  elf/dl-deps.c              |  17 ++++
>  elf/dl-fini.c              |   6 +-
>  elf/dl-init.c              |   4 +-
>  elf/dl-load.c              | 181 +++++++++++++++++++++++++++++++++----
>  elf/dl-lookup.c            |  26 +++++-
>  elf/dl-object.c            |  78 ++++++++++++++++
>  elf/dl-open.c              | 121 +++++++++++++++++++++++--
>  elf/dl-sym.c               |  14 +++
>  elf/rtld.c                 |   2 +-
>  include/link.h             |   6 +-
>  sysdeps/generic/ldsodefs.h |   7 ++
>  sysdeps/mips/bits/dlfcn.h  |  10 ++
>  14 files changed, 473 insertions(+), 52 deletions(-)
> 
> diff --git a/bits/dlfcn.h b/bits/dlfcn.h
> index f3bc63e958..1366cb3546 100644
> --- a/bits/dlfcn.h
> +++ b/bits/dlfcn.h
> @@ -32,6 +32,16 @@
>     visible as if the object were linked directly into the program.  */
>  #define RTLD_GLOBAL	0x00100
>  
> +/* If the following bit is set in the MODE argument to dlmopen
> +   then the target object is loaded into the main namespace (if
> +   it is not already there) and a shallow copy (proxy) is placed
> +   in the target namespace: This allows multiple namespaces to

I think you meant a period instead of a ':' here.

> +   share a single instance of a DSO.  */
> +#define RTLD_SHARED 0x00080
> +
> +/* Suppress RTLD_SHARED and/or DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE  */
> +#define RTLD_ISOLATE 0x00040
> +
>  /* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
>     The implementation does this by default and so we can define the
>     value to zero.  */
> diff --git a/elf/dl-close.c b/elf/dl-close.c
> index 3720e47dd1..bcdddc75c5 100644
> --- a/elf/dl-close.c
> +++ b/elf/dl-close.c
> @@ -284,8 +284,9 @@ _dl_close_worker (struct link_map *map, bool force)
>  
>  	  /* Call its termination function.  Do not do it for
>  	     half-cooked objects.  Temporarily disable exception
> -	     handling, so that errors are fatal.  */
> -	  if (imap->l_init_called)
> +	     handling, so that errors are fatal.
> +	     Proxies should never have this flag set, but we double check.  */
> +	  if (imap->l_init_called && !imap->l_proxy)
>  	    {
>  	      /* When debugging print a message first.  */
>  	      if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS,

Ok.

> @@ -361,7 +362,9 @@ _dl_close_worker (struct link_map *map, bool force)
>  	     one for the terminating NULL pointer.  */
>  	  size_t remain = (new_list != NULL) + 1;
>  	  bool removed_any = false;
> -	  for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt)
> +	  for (size_t cnt = 0;
> +               imap->l_scope && imap->l_scope[cnt] != NULL;
> +               ++cnt)
>  	    /* This relies on l_scope[] entries being always set either
>  	       to its own l_symbolic_searchlist address, or some map's
>  	       l_searchlist address.  */

Follow the already set indentation (using tabs instead of space).

> @@ -689,8 +692,10 @@ _dl_close_worker (struct link_map *map, bool force)
>  
>  	  /* We can unmap all the maps at once.  We determined the
>  	     start address and length when we loaded the object and
> -	     the `munmap' call does the rest.  */
> -	  DL_UNMAP (imap);
> +	     the `munmap' call does the rest. Proxies do not have
> +             any segments of their own to unmap.  */
> +          if (!imap->l_proxy)
> +            DL_UNMAP (imap);
>  
>  	  /* Finally, unlink the data structure and free it.  */
>  #if DL_NNS == 1

Same as before

> @@ -730,19 +735,23 @@ _dl_close_worker (struct link_map *map, bool force)
>  	    _dl_debug_printf ("\nfile=%s [%lu];  destroying link map\n",
>  			      imap->l_name, imap->l_ns);
>  
> -	  /* This name always is allocated.  */
> -	  free (imap->l_name);
> -	  /* Remove the list with all the names of the shared object.  */
> +          /* Skip structures borrowed by proxies from the real map.  */
> +          if (!imap->l_proxy)
> +            {
> +              /* This name always is allocated.  */
> +              free (imap->l_name);
> +              /* Remove the list with all the names of the shared object.  */
>  
> -	  struct libname_list *lnp = imap->l_libname;
> -	  do
> -	    {
> -	      struct libname_list *this = lnp;
> -	      lnp = lnp->next;
> -	      if (!this->dont_free)
> -		free (this);
> -	    }
> -	  while (lnp != NULL);
> +              struct libname_list *lnp = imap->l_libname;
> +              do
> +                {
> +                  struct libname_list *this = lnp;
> +                  lnp = lnp->next;
> +                  if (!this->dont_free)
> +                    free (this);
> +                }
> +              while (lnp != NULL);
> +            }
>  
>  	  /* Remove the searchlists.  */
>  	  free (imap->l_initfini);

Ok (module the indentation).

> diff --git a/elf/dl-deps.c b/elf/dl-deps.c
> index 087a49b212..b06134c512 100644
> --- a/elf/dl-deps.c
> +++ b/elf/dl-deps.c
> @@ -66,6 +66,23 @@ openaux (void *a)
>  			       ? lt_library : args->map->l_type),
>  			      args->trace_mode, args->open_mode,
>  			      args->map->l_ns);
> +
> +  /* This implies that we need to prepare a proxy in the target namespace.  */
> +  if (__glibc_unlikely (args->map->l_ns != LM_ID_BASE &&
> +                        args->aux->l_ns == LM_ID_BASE))
> +    {
> +      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
> +        _dl_debug_printf ("need proxy for file=%s [%lu]; initstate=%ld\n\n",
> +                          args->aux->l_name, args->aux->l_ns,
> +                          (long int)args->aux->l_real->l_relocated);

There is no need to cast here, since the type of bitfiels will be 'int'.

> +
> +      args->aux = _dl_new_proxy (args->aux, args->open_mode, args->map->l_ns);
> +
> +      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
> +        _dl_debug_printf ("proxying dependency=%s [%lu]; direct_opencount=%u\n\n",
> +                          args->aux->l_name, args->aux->l_ns,
> +                          args->aux->l_direct_opencount);
> +    }
>  }
>  
>  static ptrdiff_t

Ok.

> diff --git a/elf/dl-fini.c b/elf/dl-fini.c
> index 6dbdfe4b3e..10194488bb 100644
> --- a/elf/dl-fini.c
> +++ b/elf/dl-fini.c
> @@ -73,7 +73,7 @@ _dl_fini (void)
>  	  assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
>  	  for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
>  	    /* Do not handle ld.so in secondary namespaces.  */
> -	    if (l == l->l_real)
> +	    if (l == l->l_real || l->l_proxy)
>  	      {
>  		assert (i < nloaded);
>  
> @@ -111,7 +111,9 @@ _dl_fini (void)
>  	    {
>  	      struct link_map *l = maps[i];
>  
> -	      if (l->l_init_called)
> +              /* Do not call fini functions via proxies, or for
> +                 objects which are not marked as initialised.  */
> +	      if (l->l_init_called && !l->l_proxy)
>  		{
>  		  /* Make sure nothing happens if we are called twice.  */
>  		  l->l_init_called = 0;

Ok.

> diff --git a/elf/dl-init.c b/elf/dl-init.c
> index f924d26642..00ce8bb9d6 100644
> --- a/elf/dl-init.c
> +++ b/elf/dl-init.c
> @@ -30,8 +30,8 @@ call_init (struct link_map *l, int argc, char **argv, char **env)
>       need relocation, and neither do proxy objects.)  */
>    assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable);
>  
> -  if (l->l_init_called)
> -    /* This object is all done.  */
> +  if (l->l_init_called || l->l_proxy)
> +    /* This object is all done, or a proxy (and therefore initless).  */
>      return;
>  
>    /* Avoid handling this constructor again in case we have a circular

Ok.

> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 050c64135a..19b8eb64bc 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -854,6 +854,53 @@ _dl_init_paths (const char *llp, const char *source,
>      __rtld_env_path_list.dirs = (void *) -1;
>  }
>  
> +static bool
> +has_gnu_unique (int fd, const ElfW(Ehdr) *header, const ElfW(Phdr) *phdr)
> +{
> +  bool unique = false;
> +  const ElfW(Phdr) *ph;
> +
> +  for (ph = phdr; ph < &phdr[header->e_phnum]; ++ph)
> +    {
> +      off64_t end;
> +      off64_t pos;
> +      ssize_t bytes = -1;
> +      ElfW(Dyn) entry = { .d_tag = 0, .d_un.d_val = 0 };
> +
> +      switch (ph->p_type)
> +	{
> +        case PT_DYNAMIC:
> +          pos = ph->p_offset;
> +          end = pos + ph->p_filesz;
> +
> +          while (pos < end)
> +            {
> +              bytes = __pread64_nocancel (fd, &entry, sizeof (ElfW(Dyn)), pos);

Line too long.

> +
> +              if (__glibc_unlikely (bytes != sizeof (ElfW(Dyn))))
> +                goto done;

We should throw a "cannot read file data" for this case instead of use the default
LM_ID_BASE.

> +
> +              pos += bytes;
> +
> +              switch (entry.d_tag)
> +                {
> +                case DT_GNU_FLAGS_1:
> +                  unique = (entry.d_un.d_val & DF_GNU_1_UNIQUE);
> +                  goto done;

Just use 'return' here and remove the goto.

> +                  break;
> +
> +                case DT_NULL:
> +                  goto done;
> +                  break;
> +                }
> +            }
> +          break;
> +        }
> +    }
> +
> + done:
> +  return unique;
> +}
>  

I think there is no need to read the program headers again to check if the has
the gnu unique flag since on open_verify we already do it for ".note.ABI-tag".
Instead I think we should set a flag on open_verify() and use it later.

>  /* Process PT_GNU_PROPERTY program header PH in module L after
>     PT_LOAD segments are mapped.  Only one NT_GNU_PROPERTY_TYPE_0
> @@ -1037,6 +1084,32 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
>      }
>  #endif
>  
> +  /* DSOs in the main namespace which are flagged DF_GNU_1_UNIQUE should only
> +     be opened into the main namespace. Other namespaces should only get

Double space after period.

> +     proxies.  */
> +  if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE)))
> +    {
> +      /* Check base ns to see if the name matched another already loaded.  */
> +      for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL; l = l->l_next)
> +        if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id))
> +          {
> +            if (!(l->l_gnu_flags_1 & DF_GNU_1_UNIQUE))
> +              continue;
> +
> +            /* Already loaded. Bump its reference count and return it.  */
> +            __close_nocancel (fd);
> +
> +            /* If the name is not listed for this object add it.  */
> +            free (realname);
> +            add_name_to_object (l, name);
> +
> +            /* NOTE: It is important that our caller picks up on the fact
> +               that we have NOT returned an object in the requested namespace
> +               and handles the proxying correctly.  */
> +            return l;
> +          }
> +    }
> +
>    if (mode & RTLD_NOLOAD)
>      {
>        /* We are not supposed to load the object unless it is already

Ok.

> @@ -1062,8 +1135,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
>  	  && __glibc_unlikely (GLRO(dl_naudit) > 0))
>  	{
>  	  struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
> -	  /* Do not call the functions for any auditing object.  */
> -	  if (head->l_auditing == 0)
> +	  /* Do not call the functions for any auditing object.
> +	     Do not try to call auditing functions if the namespace
> +	     is currently empty. This can hapen when opening the first
> +	     DSO in a new namespace.  */
> +	  if ((head != NULL) && (head->l_auditing == 0))

Use '!head->l_auditing" to be consistent with other cases.

>  	    {
>  	      struct audit_ifaces *afct = GLRO(dl_audit);
>  	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
> @@ -1089,6 +1165,32 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
>    else
>      assert (r->r_state == RT_ADD);
>  
> +  /* Load the ELF header using preallocated struct space if it's big enough.  */
> +  maplength = header->e_phnum * sizeof (ElfW(Phdr));
> +  if (header->e_phoff + maplength <= (size_t) fbp->len)
> +    phdr = (void *) (fbp->buf + header->e_phoff);
> +  else
> +    {
> +      phdr = alloca (maplength);
> +      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
> +				       header->e_phoff) != maplength)
> +	{
> +	  errstring = N_("cannot read file data");
> +	  goto lose_errno;
> +	}
> +    }
> +

Ok, you are moving the code below to check if DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE.

> +  /* We need to check for DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE before we start
> +     initialising any namespace dependent metatada.  */
> +  if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE)))
> +    {
> +      /* Target DSO is flagged as unique: Make sure it gets loaded into
> +         the base namespace.  It is up to our caller to generate a proxy
> +         in the target nsid.  */
> +      if (has_gnu_unique (fd, header, phdr))
> +        nsid = LM_ID_BASE;
> +    }
> +
>    /* Enter the new object in the list of loaded objects.  */
>    l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
>    if (__glibc_unlikely (l == NULL))
> @@ -1106,20 +1208,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
>    type = header->e_type;
>    l->l_phnum = header->e_phnum;
>  
> -  maplength = header->e_phnum * sizeof (ElfW(Phdr));
> -  if (header->e_phoff + maplength <= (size_t) fbp->len)
> -    phdr = (void *) (fbp->buf + header->e_phoff);
> -  else
> -    {
> -      phdr = alloca (maplength);
> -      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
> -				       header->e_phoff) != maplength)
> -	{
> -	  errstring = N_("cannot read file data");
> -	  goto lose_errno;
> -	}
> -    }
> -
>     /* On most platforms presume that PT_GNU_STACK is absent and the stack is
>      * executable.  Other platforms default to a nonexecutable stack and don't
>      * need PT_GNU_STACK to do so.  */
> @@ -2027,6 +2115,37 @@ open_path (const char *name, size_t namelen, int mode,
>    return -1;
>  }
>  

Ok.

> +/* Search for a link map proxy in the given namespace by name.
> +   Consider it to be an error if the found object is not a proxy.  */
> +struct link_map *
> +_dl_find_proxy (Lmid_t nsid, const char *name)
> +{
> +  struct link_map *l;
> +
> +  for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next)
> +    {
> +      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0))
> +        continue;
> +
> +      if (!_dl_name_match_p (name, l))
> +        continue;
> +
> +      /* We have a match - stop searching.  */
> +      break;
> +    }
> +
> +  if (l != NULL)
> +    {
> +      if (l->l_proxy)
> +        return l;
> +
> +      _dl_signal_error (EEXIST, name, NULL,
> +                        N_("object cannot be demoted to a proxy"));
> +    }
> +
> +  return NULL;
> +}
> +
>  /* Search for a shared object in a given namespace.  */
>  struct link_map *
>  _dl_find_dso (const char *name, Lmid_t nsid)

Ok.

> @@ -2080,12 +2199,42 @@ _dl_map_object (struct link_map *loader, const char *name,
>  
>    assert (nsid >= 0);
>    assert (nsid < GL(dl_nns));
> +  assert (!((mode & RTLD_ISOLATE) && (mode & RTLD_SHARED)));
> +
> +#ifdef SHARED
> +  /* Only need to do proxy checks if 'nsid' is not LM_ID_BASE.  */
> +  if (__glibc_unlikely ((mode & RTLD_SHARED) && (nsid != LM_ID_BASE)))
> +    {
> +      /* Search the namespace in case the object is already proxied.  */
> +      l = _dl_find_proxy (nsid, name);
> +      if (l != NULL)
> +        return l;
> +
> +      /* Further searches should be in the base ns: We will proxy the
> +         resulting object in dl_open_worker *after* it is initialised.  */
> +      nsid = LM_ID_BASE;
> +    }
> +#endif
>  
>    /* Look for this name among those already loaded.  */
>    l = _dl_find_dso (name, nsid);
>  
>    if (l != NULL)
>      {
> +#ifdef SHARED
> +      /* If we are trying to load a DF_GNU_1_UNIQUE flagged DSO which WAS
> +         already opened in the target NS but with RTLD_ISOLATE so it WAS NOT
> +         created as a proxy we need to error out since we cannot satisfy the
> +         DF_GNU_1_UNIQUE is-equivalent-to RTLD_SHARED semantics.  */
> +      if (!(mode & RTLD_ISOLATE) &&
> +          (l->l_ns != LM_ID_BASE) &&
> +          (l->l_gnu_flags_1 & DF_GNU_1_UNIQUE) &&
> +          !l->l_proxy)
> +      {
> +        _dl_signal_error (EEXIST, name, NULL,
> +                          N_("object cannot be demoted to a proxy"));
> +      }
> +#endif
>        return l;
>      }
>  

Ok. Do we have a test for it?

> diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
> index eea217eb28..42effd8e4d 100644
> --- a/elf/dl-lookup.c
> +++ b/elf/dl-lookup.c
> @@ -24,6 +24,7 @@
>  #include <ldsodefs.h>
>  #include <dl-hash.h>
>  #include <dl-machine.h>
> +#include <dl-dst.h>
>  #include <sysdep-cancel.h>
>  #include <libc-lock.h>
>  #include <tls.h>
> @@ -917,11 +918,28 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
>  	}
>      }
>  
> +  /* Found a candidate symbol but it resides in the base namespace
> +     BUT we are in another namespace.  Therefore we want a proxy
> +     (which should already have been created by this point.  */
> +  if (__glibc_likely ((current_value.m != NULL) &&
> +                      !(IS_RTLD (current_value.m))))
> +    if (__glibc_unlikely (undef_map->l_ns != LM_ID_BASE &&
> +                          current_value.m->l_ns == LM_ID_BASE))
> +      {

I think it would be simpler to just write the comparison without trying to be too clever
with branch prediction:

  if (current_value.m != NULL && !IS_RTLD (current_value.m) 
      && undef_map->l_ns != LM_ID_BASE && current_value.m->l_name == LM_ID_BASE)


> +        struct link_map *proxy = NULL;
> +        proxy = _dl_find_proxy (undef_map->l_ns, current_value.m->l_name);

Just attribute the proxy directly:

      struct link_map *proxy = _dl_find_proxy (undef_map->l_ns,
                                               current_value.m->l_name);
> +
> +        if (proxy != NULL)
> +          current_value.m = proxy;
> +        else
> +          _dl_debug_printf ("Failed to find proxy for %s\n", current_value.m->l_name);

All _dl_debug_printf() call are only enable if uses does set LD_DEBUG, we
should do the same here:

      else if (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS)
        _dl_debug_printf ("Failed to find proxy for %s\n", current_value.m->l_name);

> +      }
> +
>    /* We have to check whether this would bind UNDEF_MAP to an object
>       in the global scope which was dynamically loaded.  In this case
>       we have to prevent the latter from being unloaded unless the
>       UNDEF_MAP object is also unloaded.  */
> -  if (__glibc_unlikely (current_value.m->l_type == lt_loaded)
> +  if (__glibc_unlikely (current_value.m->l_real->l_type == lt_loaded)
>        /* Don't do this for explicit lookups as opposed to implicit
>  	 runtime lookups.  */
>        && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0

Ok.

> @@ -935,8 +953,8 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
>  				  version, type_class, flags, skip_map);
>  
>    /* The object is used.  */
> -  if (__glibc_unlikely (current_value.m->l_used == 0))
> -    current_value.m->l_used = 1;
> +  if (__glibc_unlikely (current_value.m->l_real->l_used == 0))
> +    current_value.m->l_real->l_used = 1;
>  
>    if (__glibc_unlikely (GLRO(dl_debug_mask)
>  			& (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK)))
> @@ -944,7 +962,7 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
>  			&current_value, version, type_class, protected);
>  
>    *ref = current_value.s;
> -  return LOOKUP_VALUE (current_value.m);
> +  return LOOKUP_VALUE (current_value.m->l_real);
>  }
>  
>  

Ok.

> diff --git a/elf/dl-object.c b/elf/dl-object.c
> index 1875599eb2..30a05b086f 100644
> --- a/elf/dl-object.c
> +++ b/elf/dl-object.c
> @@ -21,6 +21,7 @@
>  #include <stdlib.h>
>  #include <unistd.h>
>  #include <ldsodefs.h>
> +#include <libintl.h>
>  
>  #include <assert.h>
>  
> @@ -50,6 +51,83 @@ _dl_add_to_namespace_list (struct link_map *new, Lmid_t nsid)
>    __rtld_lock_unlock_recursive (GL(dl_load_write_lock));
>  }
>  
> +/* Proxy an existing link map entry into a new link map:
> +   This is based on _dl_new_object, skipping the steps we know we won't need
> +   because this is mostly just a shell for the l_real pointer holding the real
> +   link map entry (normally l == l->l_real, but not for ld.so in non-main
> +   link maps or RTLD_SHARED proxies).
> +   It also flags the proxy by setting l_proxy, and sets the the no-delete
> +   flag in the original if it is an lt_loaded.  */
> +struct link_map *
> +_dl_new_proxy (struct link_map *old, int mode, Lmid_t nsid)
> +{
> +  const char *name;
> +  struct link_map *new;
> +  struct libname_list *newname;
> +#ifdef SHARED
> +  unsigned int na = GLRO(dl_naudit);
> +
> +  if ((mode & __RTLD_OPENEXEC) != 0)
> +    na = DL_NNS;
> +
> +  size_t audit_space = na * sizeof (struct auditstate);
> +#else
> +# define audit_space 0
> +#endif
> +
> +  name = old->l_name;
> +
> +  /* Find the original link map entry if 'old' is itself a proxy. */
> +  while (old != NULL && old->l_proxy)
> +    old = old->l_real;
> +
> +  if (old == NULL)
> +    _dl_signal_error (EINVAL, name, NULL, N_("cannot proxy NULL link_map"));
> +
> +  /* Object already exists in the target namespace.  This should get handled
> +     by dl_open_worker but just in case we get this far, handle it:  */
> +  if (__glibc_unlikely (old->l_ns == nsid))
> +    _dl_signal_error (EEXIST, name, NULL,
> +                      N_("existing object cannot be demoted to a proxy"));
> +
> +  /* Now duplicate as little of _dl_new_object as possible to get a
> +     working proxied object in the target link map.  */
> +  new = (struct link_map *) calloc (sizeof (*new) + audit_space
> +                                    + sizeof (struct link_map *)
> +                                    + sizeof (*newname) + PATH_MAX, 1);
> +
> +  if (new == NULL)
> +    _dl_signal_error (ENOMEM, name, NULL,
> +                      N_("cannot create shared object descriptor"));
> +
> +  /* Specific to the proxy.  */
> +  new->l_real = old;
> +  new->l_proxy = 1;
> +  new->l_ns = nsid;
> +
> +  /* Copied from the origin.  */
> +  new->l_libname = old->l_libname;
> +  new->l_name = old->l_name;
> +  /* Proxies are considered lt_loaded if the real entry type is lt_library.  */
> +  new->l_type = (old->l_type == lt_library) ? lt_loaded : old->l_type;
> +
> +  if (__glibc_unlikely (mode & RTLD_NODELETE))
> +    new->l_flags_1 |= DF_1_NODELETE;
> +
> +  /* Specific to the origin.  Ideally we'd do some accounting here but
> +     for now it's easier to pin the original so the proxy remains valid.  */
> +  if (old->l_type == lt_loaded)
> +    old->l_flags_1 |= DF_1_NODELETE;
> +
> +  /* Fix up the searchlist so that relocations work.  */
> +  _dl_map_object_deps (new, NULL, 0, 0,
> +		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
> +
> +  /* And finally put the proxy in the target namespace.  */
> +  _dl_add_to_namespace_list (new, nsid);
> +
> +  return new;
> +}
>  
>  /* Allocate a `struct link_map' for a new object being loaded,
>     and enter it into the _dl_loaded list.  */

Ok.

> diff --git a/elf/dl-open.c b/elf/dl-open.c
> index a066f39bd0..8eed817980 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -488,6 +488,16 @@ dl_open_worker (void *a)
>    const char *file = args->file;
>    int mode = args->mode;
>    struct link_map *call_map = NULL;
> +  struct link_map *preloaded = NULL;
> +  bool want_proxy = false;
> +  bool dl_isolate = mode & RTLD_ISOLATE;
> +  Lmid_t proxy_ns = LM_ID_BASE;
> +
> +  /* Isolation means we should suppress all inter-namespace sharing.  */
> +  if (dl_isolate)
> +    mode &= ~RTLD_SHARED;
> +  else
> +    want_proxy = mode & RTLD_SHARED;
>  
>    /* Determine the caller's map if necessary.  This is needed in case
>       we have a DST, when we don't know the namespace ID we have to put

Ok.

> @@ -512,6 +522,15 @@ dl_open_worker (void *a)
>  	args->nsid = call_map->l_ns;
>      }
>  
> +  /* Now that we know the NS for sure, sanity check the mode.  */
> +  if (__glibc_likely (args->nsid == LM_ID_BASE) &&
> +      __glibc_unlikely (mode & RTLD_SHARED))
> +    {
> +      args->mode &= ~RTLD_SHARED;
> +      mode &= ~RTLD_SHARED;
> +      want_proxy = 0;
> +    }
> +
>    /* The namespace ID is now known.  Keep track of whether libc.so was
>       already loaded, to determine whether it is necessary to call the
>       early initialization routine (or clear libc_map on error).  */

Ok.

> @@ -525,6 +544,24 @@ dl_open_worker (void *a)
>       may not be true if this is a recursive call to dlopen.  */
>    _dl_debug_initialize (0, args->nsid);
>  
> +  /* Target Lmid is not the base and we haven't explicitly asked for a proxy:
> +     We need to check for a matching DSO in the base Lmid in case it is flagged
> +     DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE in which case we add RTLD_SHARED to the
> +     mode and set want_proxy.
> +     NOTE: RTLD_ISOLATE in the mode suppresses this behaviour.  */
> +  if (__glibc_unlikely (args->nsid != LM_ID_BASE) &&
> +      __glibc_likely (!dl_isolate) &&
> +      __glibc_likely (!want_proxy))
> +    {
> +      preloaded = _dl_find_dso (file, LM_ID_BASE);
> +
> +      if ((preloaded != NULL) && (preloaded->l_gnu_flags_1 & DF_GNU_1_UNIQUE))
> +        {
> +          want_proxy = true;
> +          mode |= RTLD_SHARED;
> +        }
> +    }
> +
>    /* Load the named object.  */
>    struct link_map *new;
>    args->map = new = _dl_map_object (call_map, file, lt_loaded, 0,

Ok.

> @@ -545,6 +582,35 @@ dl_open_worker (void *a)
>    /* This object is directly loaded.  */
>    ++new->l_direct_opencount;
>  
> +  /* Proxy already existed in the target ns, nothing left to do.  */
> +  if (__glibc_unlikely (new->l_proxy))
> +    {
> +      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
> +	_dl_debug_printf ("proxied file=%s [%lu]; direct_opencount=%u\n\n",
> +			  new->l_name, new->l_ns, new->l_direct_opencount);
> +      return;
> +    }
> +
> +  /* If we are trying to load a DF_GNU_1_UNIQUE flagged DSO which was
> +     NOT ALREADY LOADED (or not loaded with the name we are using) then
> +     _dl_map_object will have returned an instance from the main namespace.
> +     We need to detect this and set up the RTLD_SHARED flags.  */
> +  if (__glibc_unlikely (args->nsid != LM_ID_BASE && new->l_ns == LM_ID_BASE))
> +    {
> +      want_proxy = RTLD_SHARED;
> +      mode |= RTLD_SHARED;
> +    }
> +
> +  /* If we want proxy and we get this far then the entry in 'new' will
> +     be in the main namespace: we should revert to the main namespace code
> +     path(s), but remember the namespace we want the proxy to be in.  */
> +  if (__glibc_unlikely (want_proxy))
> +    {
> +      proxy_ns = args->nsid;
> +      args->nsid = LM_ID_BASE;
> +      args->libc_already_loaded = GL(dl_ns)[LM_ID_BASE].libc_map != NULL;
> +    }
> +
>    /* It was already open.  */
>    if (__glibc_unlikely (new->l_searchlist.r_list != NULL))
>      {

Ok.

> @@ -576,6 +642,16 @@ dl_open_worker (void *a)
>  
>        assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
>  
> +      if (__glibc_unlikely (want_proxy))
> +        {
> +          args->map = new = _dl_new_proxy (new, mode, proxy_ns);
> +          ++new->l_direct_opencount;
> +
> +          if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
> +            _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n",
> +                              new->l_name, new->l_ns, new->l_direct_opencount);
> +        }
> +
>        return;
>      }
>  

Ok.

> @@ -586,7 +662,8 @@ dl_open_worker (void *a)
>  
>    /* Load that object's dependencies.  */
>    _dl_map_object_deps (new, NULL, 0, 0,
> -		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
> +		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT |
> +		               RTLD_ISOLATE));
>  
>    /* So far, so good.  Now check the versions.  */
>    for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)

Ok.

> @@ -680,7 +757,10 @@ dl_open_worker (void *a)
>      {
>        l = new->l_initfini[i];
>  
> -      if (l->l_real->l_relocated)
> +      if (l->l_proxy)
> +          l = l->l_real;
> +
> +      if (l->l_relocated)
>  	continue;
>  
>        if (! relocation_in_progress)


Ok.

> @@ -769,16 +849,21 @@ dl_open_worker (void *a)
>       namespace.  */
>    if (!args->libc_already_loaded)
>      {
> +      /* If this is a secondary (nsid != LM_ID_BASE) namespace then
> +         it is POSSIBLE there's no libc_map at all - We use the one
> +         shared with LM_ID_BASE instead (which MUST already be
> +         initialised for us to even reach here).  */
>        struct link_map *libc_map = GL(dl_ns)[args->nsid].libc_map;
>  #ifdef SHARED
> -      bool initial = libc_map->l_ns == LM_ID_BASE;
> +      bool initial = (libc_map != NULL) && (libc_map->l_real->l_ns == LM_ID_BASE);
>  #else
>        /* In the static case, there is only one namespace, but it
>  	 contains a secondary libc (the primary libc is statically
>  	 linked).  */
>        bool initial = false;
>  #endif
> -      _dl_call_libc_early_init (libc_map, initial);
> +      if (libc_map != NULL)
> +        _dl_call_libc_early_init (libc_map, initial);
>      }
>  

This is already fixed by 3908fa933a4354.

>    /* Run the initializer functions of new objects.  Temporarily
> @@ -799,10 +884,27 @@ dl_open_worker (void *a)
>    if (mode & RTLD_GLOBAL)
>      add_to_global_update (new);
>  
> +  if (__glibc_unlikely (want_proxy))
> +    {
> +      /* args->map is the return slot which the caller sees, but keep
> +         the original value of new hanging around so we can see the
> +         real link map entry (for logging etc).  */
> +      args->map = _dl_new_proxy (new, mode, proxy_ns);
> +      ++args->map->l_direct_opencount;
> +    }
> +
>    /* Let the user know about the opencount.  */
>    if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
> -    _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
> -		      new->l_name, new->l_ns, new->l_direct_opencount);
> +    {
> +      _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
> +                        new->l_name, new->l_ns, new->l_direct_opencount);
> +
> +      if (args->map->l_proxy)
> +        _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n",
> +                          args->map->l_name,
> +                          args->map->l_ns,
> +                          args->map->l_direct_opencount);
> +    }
>  }
>  

Ok.

>  void *
> @@ -891,8 +993,11 @@ no more namespaces available for dlmopen()"));
>    if (__glibc_unlikely (exception.errstring != NULL))
>      {
>        /* Avoid keeping around a dangling reference to the libc.so link
> -	 map in case it has been cached in libc_map.  */
> -      if (!args.libc_already_loaded)
> +	 map in case it has been cached in libc_map.
> +         If this is a secondary namespace opened with LM_ID_NEWLM and
> +         WITHOUT RTLD_ISOLATE then nsid can still be -1 (LM_ID_NEWLM)
> +         at this point.  */
> +      if (!args.libc_already_loaded && nsid >= LM_ID_BASE)
>  	GL(dl_ns)[nsid].libc_map = NULL;
>  
>        /* Remove the object from memory.  It may be in an inconsistent

Ok.

> diff --git a/elf/dl-sym.c b/elf/dl-sym.c
> index fa0cce678f..966079915a 100644
> --- a/elf/dl-sym.c
> +++ b/elf/dl-sym.c
> @@ -96,6 +96,10 @@ do_sym (void *handle, const char *name, void *who,
>      {
>        match = _dl_sym_find_caller_link_map (caller);
>  
> +      /* Proxies don't contain any symbols: Need to look at the real DSO.  */
> +      if (__glibc_unlikely (match->l_proxy))
> +	match = match->l_real;
> +
>        /* Search the global scope.  We have the simple case where
>  	 we look up in the scope of an object which was part of
>  	 the initial binary.  And then the more complex part

Ok.

> @@ -140,6 +144,11 @@ RTLD_NEXT used in code not dynamically loaded"));
>  	}
>  
>        struct link_map *l = match;
> +
> +      /* Proxies don't contain any symbols: Need to look at the real DSO.  */
> +      if (__glibc_unlikely (l->l_proxy))
> +	l = l->l_real;
> +
>        while (l->l_loader != NULL)
>  	l = l->l_loader;
>  
> @@ -150,6 +159,11 @@ RTLD_NEXT used in code not dynamically loaded"));
>      {
>        /* Search the scope of the given object.  */
>        struct link_map *map = handle;
> +
> +      /* Proxies don't contain any symbols: Need to look at the real DSO.  */
> +      if (__glibc_unlikely (map->l_proxy))
> +	map = map->l_real;
> +
>        result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope,
>  					 vers, 0, flags, NULL);
>      }


Ok.

> diff --git a/elf/rtld.c b/elf/rtld.c
> index fbbd60b446..9246200be1 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -661,7 +661,7 @@ dlmopen_doit (void *a)
>    struct dlmopen_args *args = (struct dlmopen_args *) a;
>    args->map = _dl_open (args->fname,
>  			(RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT
> -			 | __RTLD_SECURE),
> +			 | __RTLD_SECURE | RTLD_ISOLATE),
>  			dl_main, LM_ID_NEWLM, _dl_argc, _dl_argv,
>  			__environ);
>  }

Ok.

> diff --git a/include/link.h b/include/link.h
> index 6ed35eb808..55e0cad71d 100644
> --- a/include/link.h
> +++ b/include/link.h
> @@ -107,8 +107,9 @@ struct link_map
>         They may change without notice.  */
>  
>      /* This is an element which is only ever different from a pointer to
> -       the very same copy of this type for ld.so when it is used in more
> -       than one namespace.  */
> +       the very same copy of this type when:
> +       - A shallow copy of ld.so is placed in namespaces other than LM_ID_BASE.
> +       - An object is proxied into a namespace by dlmopen with RTLD_SHARED.  */
>      struct link_map *l_real;
>  
>      /* Number of the namespace this link map belongs to.  */
> @@ -180,6 +181,7 @@ struct link_map
>      unsigned int l_relocated:1;	/* Nonzero if object's relocations done.  */
>      unsigned int l_init_called:1; /* Nonzero if DT_INIT function called.  */
>      unsigned int l_global:1;	/* Nonzero if object in _dl_global_scope.  */
> +    unsigned int l_proxy:1;    /* Nonzero if object is a shallow copy.  */
>      unsigned int l_reserved:2;	/* Reserved for internal use.  */
>      unsigned int l_phdr_allocated:1; /* Nonzero if the data structure pointed
>  					to by `l_phdr' is allocated.  */

Ok.

> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
> index 44e7097712..66cfc1deb3 100644
> --- a/sysdeps/generic/ldsodefs.h
> +++ b/sysdeps/generic/ldsodefs.h
> @@ -1015,6 +1015,11 @@ extern lookup_t _dl_lookup_symbol_x (const char *undef,
>  				     struct link_map *skip_map)
>       attribute_hidden;
>  
> +/* Proxy an existing link map entry into a new link map */
> +extern struct link_map *_dl_new_proxy (struct link_map *old,
> +				       int mode,
> +				       Lmid_t nsid)
> +     attribute_hidden;
>  
>  /* Restricted version of _dl_lookup_symbol_x.  Searches MAP (and only
>     MAP) for the symbol UNDEF_NAME, with GNU hash NEW_HASH (computed
> @@ -1290,6 +1295,8 @@ rtld_hidden_proto (_dl_find_dso_for_object)
>  extern struct link_map *_dl_find_dso (const char *name, Lmid_t nsid);
>  rtld_hidden_proto (_dl_find_dso)
>  
> +extern struct link_map *_dl_find_proxy (Lmid_t nsid, const char *name);
> +rtld_hidden_proto (_dl_find_proxy)
>  
>  /* Initialization which is normally done by the dynamic linker.  */
>  extern void _dl_non_dynamic_init (void)

Ok.

> diff --git a/sysdeps/mips/bits/dlfcn.h b/sysdeps/mips/bits/dlfcn.h
> index 5cec898de3..898797f863 100644
> --- a/sysdeps/mips/bits/dlfcn.h
> +++ b/sysdeps/mips/bits/dlfcn.h
> @@ -32,6 +32,16 @@
>     visible as if the object were linked directly into the program.  */
>  #define RTLD_GLOBAL	0x0004
>  
> +/* If the following bit is set in the MODE argument to dlmopen
> +   then the target object is loaded into the main namespace (if
> +   it is not already there) and a shallow copy (proxy) is placed
> +   in the target namespace: This allows multiple namespaces to
> +   share a single instance of a DSO.  */
> +#define RTLD_SHARED 0x00020
> +
> +/* Suppress RTLD_SHARED and/or DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE  */
> +#define RTLD_ISOLATE 0x00040
> +
>  /* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
>     The implementation does this by default and so we can define the
>     value to zero.  */
> 

Ok.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 7/8] Restore separate libc loading for the TLS/namespace storage test
  2021-07-08 16:32 ` [RFC][PATCH v12 7/8] Restore separate libc loading for the TLS/namespace storage test Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 19:08   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 19:08 UTC (permalink / raw)
  To: Vivek Das Mohapatra, libc-alpha



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> tst-tls-ie-dlmopen checks to see that new namespaces consume
> TLS memory as expected: This does not happen when new namespaces
> share the same libc instance (since TLS is allocated only when
> a new libc instance insitialises its threading infrastructure).
> 
> Adding RTLD_ISOLATE to the dlmopen flags in the test restores
> the old behaviour which allows the test to check what it
> actually needs to.

LGTM, but this patch should be merged with the
'Implement dlmopen RTLD_SHARED flag (bug 22745)' one to be logically
consistent.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  elf/tst-tls-ie-dlmopen.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/elf/tst-tls-ie-dlmopen.c b/elf/tst-tls-ie-dlmopen.c
> index a579d72d2d..e67017db2d 100644
> --- a/elf/tst-tls-ie-dlmopen.c
> +++ b/elf/tst-tls-ie-dlmopen.c
> @@ -53,7 +53,7 @@ static void *
>  load_and_access (Lmid_t lmid, const char *mod, const char *func)
>  {
>    /* Load module with TLS.  */
> -  void *p = xdlmopen (lmid, mod, RTLD_NOW);
> +  void *p = xdlmopen (lmid, mod, RTLD_NOW|RTLD_ISOLATE);
>    /* Access the TLS variable to ensure it is allocated.  */
>    void (*f) (void) = (void (*) (void))xdlsym (p, func);
>    f ();
> @@ -95,7 +95,7 @@ do_test (void)
>       than 1024 bytes are available (exact number depends on TLS optimizations
>       and the libc TLS use).  */
>    printf ("The next dlmopen should fail...\n");
> -  void *p = dlmopen (LM_ID_BASE, "tst-tls-ie-mod4.so", RTLD_NOW);
> +  void *p = dlmopen (LM_ID_BASE, "tst-tls-ie-mod4.so", RTLD_NOW|RTLD_ISOLATE);
>    if (p != NULL)
>      FAIL_EXIT1 ("error: expected dlmopen to fail because there is "
>  		"not enough surplus static TLS.\n");
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 6/8] Add dlmopen / RTLD_SHARED tests
  2021-07-08 16:32 ` [RFC][PATCH v12 6/8] Add dlmopen / RTLD_SHARED tests Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 20:09   ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 20:09 UTC (permalink / raw)
  To: Vivek Das Mohapatra, libc-alpha



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> We ensure a DF_GNU_1_UNIQUE library is loaded (as a DT_NEEDED
> dependency) into the target namespace by the test module.
> 
> This checks that the code path for dependencies honours the flag
> and prepares a base namespace library with a proxy in the target
> namespace (since the mechanism for this is not the same as for
> DSOs that have already been loaded into the base namespace).

Patch look good in general, with some comments below. Some are due the
missing rebase against master, specially after the libdl move to libc,
but mostly are to adjust some tests internals.

> ---
>  elf/Makefile                         | 106 +++-
>  elf/tst-dlmopen-auditmod.c           |  23 +
>  elf/tst-dlmopen-common.h             |  33 +
>  elf/tst-dlmopen-main.h               | 879 +++++++++++++++++++++++++++
>  elf/tst-dlmopen-modules.h            |  21 +
>  elf/tst-dlmopen-rtld-audit-shared1.c |   8 +
>  elf/tst-dlmopen-rtld-audit-shared2.c |   8 +
>  elf/tst-dlmopen-rtld-audit-shared3.c |   7 +
>  elf/tst-dlmopen-rtld-audit-shared4.c |   8 +
>  elf/tst-dlmopen-rtld-audit-shared5.c |   8 +
>  elf/tst-dlmopen-rtld-audit-shared6.c |   8 +
>  elf/tst-dlmopen-rtld-audit-unique1.c |   7 +
>  elf/tst-dlmopen-rtld-audit-unique2.c |   7 +
>  elf/tst-dlmopen-rtld-audit-unique3.c |   7 +
>  elf/tst-dlmopen-rtld-audit-unique4.c |   7 +
>  elf/tst-dlmopen-rtld-audit-unique5.c |   7 +
>  elf/tst-dlmopen-rtld-audit-unique6.c |   7 +
>  elf/tst-dlmopen-rtld-shared1.c       |   7 +
>  elf/tst-dlmopen-rtld-shared1.h       |  64 ++
>  elf/tst-dlmopen-rtld-shared2.c       |   7 +
>  elf/tst-dlmopen-rtld-shared2.h       |  66 ++
>  elf/tst-dlmopen-rtld-shared3.c       |   7 +
>  elf/tst-dlmopen-rtld-shared3.h       |  43 ++
>  elf/tst-dlmopen-rtld-shared4.c       |   7 +
>  elf/tst-dlmopen-rtld-shared4.h       |  14 +
>  elf/tst-dlmopen-rtld-shared5.c       |   7 +
>  elf/tst-dlmopen-rtld-shared5.h       |  25 +
>  elf/tst-dlmopen-rtld-shared6.c       |   7 +
>  elf/tst-dlmopen-rtld-shared6.h       |  36 ++
>  elf/tst-dlmopen-rtld-unique1.c       |   7 +
>  elf/tst-dlmopen-rtld-unique1.h       |  86 +++
>  elf/tst-dlmopen-rtld-unique2.c       |   7 +
>  elf/tst-dlmopen-rtld-unique2.h       |  25 +
>  elf/tst-dlmopen-rtld-unique3.c       |   7 +
>  elf/tst-dlmopen-rtld-unique3.h       |  13 +
>  elf/tst-dlmopen-rtld-unique4.c       |   7 +
>  elf/tst-dlmopen-rtld-unique4.h       |  14 +
>  elf/tst-dlmopen-rtld-unique5.c       |   7 +
>  elf/tst-dlmopen-rtld-unique5.h       |  58 ++
>  elf/tst-dlmopen-rtld-unique6.c       |   7 +
>  elf/tst-dlmopen-rtld-unique6.h       |  51 ++
>  elf/tst-dlmopen-sharedmod-norm.c     |  34 ++
>  elf/tst-dlmopen-sharedmod-uniq.c     |  33 +
>  elf/tst-dlmopen-std-do-test.c        |  12 +
>  44 files changed, 1808 insertions(+), 1 deletion(-)
>  create mode 100644 elf/tst-dlmopen-auditmod.c
>  create mode 100644 elf/tst-dlmopen-common.h
>  create mode 100644 elf/tst-dlmopen-main.h
>  create mode 100644 elf/tst-dlmopen-modules.h
>  create mode 100644 elf/tst-dlmopen-rtld-audit-shared1.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-shared2.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-shared3.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-shared4.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-shared5.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-shared6.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-unique1.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-unique2.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-unique3.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-unique4.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-unique5.c
>  create mode 100644 elf/tst-dlmopen-rtld-audit-unique6.c
>  create mode 100644 elf/tst-dlmopen-rtld-shared1.c
>  create mode 100644 elf/tst-dlmopen-rtld-shared1.h
>  create mode 100644 elf/tst-dlmopen-rtld-shared2.c
>  create mode 100644 elf/tst-dlmopen-rtld-shared2.h
>  create mode 100644 elf/tst-dlmopen-rtld-shared3.c
>  create mode 100644 elf/tst-dlmopen-rtld-shared3.h
>  create mode 100644 elf/tst-dlmopen-rtld-shared4.c
>  create mode 100644 elf/tst-dlmopen-rtld-shared4.h
>  create mode 100644 elf/tst-dlmopen-rtld-shared5.c
>  create mode 100644 elf/tst-dlmopen-rtld-shared5.h
>  create mode 100644 elf/tst-dlmopen-rtld-shared6.c
>  create mode 100644 elf/tst-dlmopen-rtld-shared6.h
>  create mode 100644 elf/tst-dlmopen-rtld-unique1.c
>  create mode 100644 elf/tst-dlmopen-rtld-unique1.h
>  create mode 100644 elf/tst-dlmopen-rtld-unique2.c
>  create mode 100644 elf/tst-dlmopen-rtld-unique2.h
>  create mode 100644 elf/tst-dlmopen-rtld-unique3.c
>  create mode 100644 elf/tst-dlmopen-rtld-unique3.h
>  create mode 100644 elf/tst-dlmopen-rtld-unique4.c
>  create mode 100644 elf/tst-dlmopen-rtld-unique4.h
>  create mode 100644 elf/tst-dlmopen-rtld-unique5.c
>  create mode 100644 elf/tst-dlmopen-rtld-unique5.h
>  create mode 100644 elf/tst-dlmopen-rtld-unique6.c
>  create mode 100644 elf/tst-dlmopen-rtld-unique6.h
>  create mode 100644 elf/tst-dlmopen-sharedmod-norm.c
>  create mode 100644 elf/tst-dlmopen-sharedmod-uniq.c
>  create mode 100644 elf/tst-dlmopen-std-do-test.c
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 9734030c23..4cfd79066a 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -196,6 +196,38 @@ static-dlopen-environment = \
>  tst-tls9-static-ENV = $(static-dlopen-environment)
>  tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment)
>  
> +dlmopen-rtld-tests-norm := \
> +	 tst-dlmopen-rtld-shared1 \
> +	 tst-dlmopen-rtld-shared2 \
> +	 tst-dlmopen-rtld-shared3 \
> +	 tst-dlmopen-rtld-shared4 \
> +	 tst-dlmopen-rtld-shared5 \
> +	 tst-dlmopen-rtld-shared6
> +
> +dlmopen-rtld-tests-uniq := \
> +	 tst-dlmopen-rtld-unique1 \
> +	 tst-dlmopen-rtld-unique2 \
> +	 tst-dlmopen-rtld-unique3 \
> +	 tst-dlmopen-rtld-unique4 \
> +	 tst-dlmopen-rtld-unique5 \
> +	 tst-dlmopen-rtld-unique6
> +
> +dlmopen-rtld-audit-tests-norm := \
> +	 tst-dlmopen-rtld-audit-shared1 \
> +	 tst-dlmopen-rtld-audit-shared2 \
> +	 tst-dlmopen-rtld-audit-shared3 \
> +	 tst-dlmopen-rtld-audit-shared4 \
> +	 tst-dlmopen-rtld-audit-shared5 \
> +	 tst-dlmopen-rtld-audit-shared6
> +
> +dlmopen-rtld-audit-tests-uniq := \
> +	 tst-dlmopen-rtld-audit-unique1 \
> +	 tst-dlmopen-rtld-audit-unique2 \
> +	 tst-dlmopen-rtld-audit-unique3 \
> +	 tst-dlmopen-rtld-audit-unique4 \
> +	 tst-dlmopen-rtld-audit-unique5 \
> +	 tst-dlmopen-rtld-audit-unique6
> +
>  tests += restest1 preloadtest loadfail multiload origtest resolvfail \
>  	 constload1 order noload filter \
>  	 reldep reldep2 reldep3 reldep4 nodelete nodelete2 \

Ok.

> @@ -233,7 +265,11 @@ tests-internal += loadtest unload unload2 circleload1 \
>  	 neededtest neededtest2 neededtest3 neededtest4 \
>  	 tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \
>  	 tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \
> -	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split
> +	 tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split \
> +	 $(dlmopen-rtld-tests-norm) \
> +	 $(dlmopen-rtld-tests-uniq) \
> +	 $(dlmopen-rtld-audit-tests-norm) \
> +	 $(dlmopen-rtld-audit-tests-uniq)
>  tests-container += tst-pldd tst-dlopen-tlsmodid-container \
>    tst-dlopen-self-container tst-preload-pthread-libc
>  test-srcs = tst-pathopt

Ok.

> @@ -288,6 +324,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
>  		$(modules-execstack-$(have-z-execstack)) \
>  		tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \
>  		tst-dlmopen1mod tst-auditmod1 \
> +		tst-dlmopen-sharedmod-norm \
> +		tst-dlmopen-sharedmod-uniq \
> +		tst-dlmopen-auditmod \
>  		unload3mod1 unload3mod2 unload3mod3 unload3mod4 \
>  		unload4mod1 unload4mod2 unload4mod3 unload4mod4 \
>  		unload6mod1 unload6mod2 unload6mod3 \

Ok.

> @@ -327,6 +366,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
>  		tst-tlsalign-lib tst-nodelete-opened-lib tst-nodelete2mod \
>  		tst-audit11mod1 tst-audit11mod2 tst-auditmod11 \
>  		tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12 \
> +		tst-dlmopen-auditmod \
>  		tst-latepthreadmod $(tst-tls-many-dynamic-modules) \
>  		tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \
>  		tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
> @@ -819,6 +859,9 @@ tst-nodelete-uniquemod.so-no-z-defs = yes
>  tst-nodelete-rtldmod.so-no-z-defs = yes
>  tst-nodelete-zmod.so-no-z-defs = yes
>  tst-nodelete2mod.so-no-z-defs = yes
> +tst-dlmopen-sharedmod-norm.so-no-z-defs = yes
> +tst-dlmopen-sharedmod-uniq.so-no-z-defs = yes
> +tst-dlmopen-auditmod.so-no-z-defs = yes
>  
>  ifeq ($(build-shared),yes)
>  # Build all the modules even when not actually running test programs.

Ok.

> @@ -1235,6 +1278,67 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
>  
>  $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
>  
> +ifneq ($(ld-zunique),yes)
> +$(objpfx)tst-dlmopen-sharedmod-uniq.so: $(common-objpfx)elf/dynamic-notes.os
> +else
> +LDFLAGS-tst-dlmopen-sharedmod-uniq.so = -Wl,-z,unique
> +endif
> +

Ok.

> +# Add a DT_NEEDED entry for a DF_GNU_1_UNIQUE library to the test modules.
> +LDFLAGS-tst-dlmopen-sharedmod-norm.so += -Wl,-lrt
> +LDFLAGS-tst-dlmopen-sharedmod-uniq.so += -Wl,-lrt

Ok, but I think we might revise it since librt is just a shim library
for caomtibility.  Maybe add another library to force a DT_NEEDED.

> +
> +$(objpfx)tst-dlmopen-sharedmod-norm.so: $(libdl)
> +$(objpfx)tst-dlmopen-sharedmod-uniq.so: $(libdl)
> +$(objpfx)tst-dlmopen-auditmod.so: $(libdl)

There is no need for these anymore, now that libdl symbols are provided
by libc.

> +
> +dlmopen-rtld-tests-norm-executables := \
> +	 $(foreach x,$(dlmopen-rtld-tests-norm),$(objpfx)$(x))
> +dlmopen-rtld-tests-norm-out := \
> +	 $(foreach x,$(dlmopen-rtld-tests-norm),$(objpfx)$(x).out)
> +
> +dlmopen-rtld-tests-uniq-executables := \
> +	 $(foreach x,$(dlmopen-rtld-tests-uniq),$(objpfx)$(x))
> +dlmopen-rtld-tests-uniq-out := \
> +	 $(foreach x,$(dlmopen-rtld-tests-uniq),$(objpfx)$(x).out)
> +
> +dlmopen-rtld-audit-tests-norm-executables := \
> +	 $(foreach x,$(dlmopen-rtld-audit-tests-norm),$(objpfx)$(x))
> +dlmopen-rtld-audit-tests-norm-out := \
> +	 $(foreach x,$(dlmopen-rtld-audit-tests-norm),$(objpfx)$(x).out)
> +
> +dlmopen-rtld-audit-tests-uniq-executables := \
> +	 $(foreach x,$(dlmopen-rtld-audit-tests-uniq),$(objpfx)$(x))
> +dlmopen-rtld-audit-tests-uniq-out := \
> +	 $(foreach x,$(dlmopen-rtld-audit-tests-uniq),$(objpfx)$(x).out)
> +
> +$(dlmopen-rtld-tests-norm-executables): $(libdl)
> +$(dlmopen-rtld-tests-norm-out): $(objpfx)tst-dlmopen-sharedmod-norm.so
> +
> +$(dlmopen-rtld-tests-uniq-executables): $(libdl)
> +$(dlmopen-rtld-tests-uniq-out): $(objpfx)tst-dlmopen-sharedmod-uniq.so
> +
> +

Ok.

> +$(dlmopen-rtld-audit-tests-norm-executables): $(libdl)

There is no need for these anymore.

> +$(dlmopen-rtld-audit-tests-norm-out): $(objpfx)tst-dlmopen-sharedmod-norm.so
> +$(dlmopen-rtld-audit-tests-norm-out): $(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-shared1-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-shared2-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-shared3-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-shared4-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-shared5-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-shared6-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +
> +$(dlmopen-rtld-audit-tests-uniq-executables): $(libdl)

There is no need for these anymore.

> +$(dlmopen-rtld-audit-tests-uniq-out): $(objpfx)tst-dlmopen-sharedmod-uniq.so
> +$(dlmopen-rtld-audit-tests-uniq-out): $(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-unique1-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-unique2-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-unique3-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-unique4-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-unique5-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +tst-dlmopen-rtld-audit-unique6-ENV = LD_AUDIT=$(objpfx)tst-dlmopen-auditmod.so
> +
>  $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
>  tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
>  

Ok.

> diff --git a/elf/tst-dlmopen-auditmod.c b/elf/tst-dlmopen-auditmod.c
> new file mode 100644
> index 0000000000..d630a26cd6
> --- /dev/null
> +++ b/elf/tst-dlmopen-auditmod.c
> @@ -0,0 +1,23 @@
> +/* Audit module for tst-dlmopen-rtld-audit-*
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +unsigned int
> +la_version (unsigned int version)
> +{
> +  return version;
> +}

Ok.

> diff --git a/elf/tst-dlmopen-common.h b/elf/tst-dlmopen-common.h
> new file mode 100644
> index 0000000000..477e2e30b5
> --- /dev/null
> +++ b/elf/tst-dlmopen-common.h
> @@ -0,0 +1,33 @@
> +/* Common infrastructure for tst-dlmopen-rtld-*
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#pragma once

No pragma once, as discussed before there are unreliable.

> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <gnu/lib-names.h>
> +
> +typedef struct
> +{
> +  const char *name;
> +  void *free;
> +  void *timer_create;
> +} dlmopen_testresult;
> +
> +typedef dlmopen_testresult * (*dlmopen_testfunc) (void);

Ok.

> diff --git a/elf/tst-dlmopen-main.h b/elf/tst-dlmopen-main.h
> new file mode 100644
> index 0000000000..8baf160d6c
> --- /dev/null
> +++ b/elf/tst-dlmopen-main.h
> @@ -0,0 +1,879 @@
> +/* Main infrastructure for tst-dlmopen-rtld-*
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#pragma once

Ditto.

> +
> +/* dlmopen +/- RTLD_SHARED/RTLD_ISOLATE semantics:
> +
> +   RTLD_ISOLATE's purpose is to suppress all shared behaviour,
> +   mainly used for LD_AUDIT code paths but available to the user
> +   and also useful for constructing test case preconditions.
> +
> +   dlmopen should have the following behaviour:
> +
> +   Notation:
> +        Number of namespace ('+' means make a new one in an empty NS)
> +        |
> +     [+]X[p] - p indicates a proxy
> +      |
> +      + -> new enry after the dlmopen call

s/enry/entry

> +
> +      Need to be able to inspect:
> +
> +      list of dl handles before we start (base state)
> +      list of dl handles after an action in each namespace
> +      ns of a given dl handle
> +      _real_ ns of a given handle (ie where does a proxy point)
> +
> +      In the tables below each line represents a separate test.
> +
> +      In practice many tests can be implemented in the same executable
> +      as some tests set up the required preconditions for other tests
> +      if they complete successfully.
> +
> +      Target is a normal DSO:
> +      Before | Target NS | RTLD Flags | After  | handle NS | libc NS
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-shared1:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | 0         | -          | +0     | 0         | 0
> +       0     | 0         | -          | 0      | 0         | 0
> +       0     | 0         | SHARED     | 0      | 0         | 0
> +       0     | +         | -          | 0,+1   | 1         | 0
> +       0,1   | 0         | -          | 0,1    | 0         | 0
> +       0,1   | 0         | SHARED     | 0,1    | 0         | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-shared2:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | 0         | SHARED     | +0     | 0         | 0
> +       0     | +         | SHARED     | 0,+1p  | 1p        | 0
> +       0,1p  | 0         | -          | 0,1p   | 0         | 0
> +       0,1p  | 0         | SHARED     | 0,1p   | 0         | 0
> +       0,1p  | 1         | -          | 0,1p   | 1p        | 0
> +       0,1p  | 1         | SHARED     | 0,1p   | 1p        | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-shared3
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | +         | -          | +1     | 1         | 0
> +       1     | 0         | -          | +0,1   | 0         | 0
> +       0,1   | 1         | -          | 0,1    | 1         | 0
> +       0,1   | 1         | SHARED     | 0,1    | ERR       | -
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-shared4
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | +         | SHARED     | +0,+1p | 1p        | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-shared5
> +      -------+-----------+------------+--------+-----------+---------
> +       1     | 0         | SHARED     | +0,1   | 0         | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-shared6
> +      -------+-----------+------------+--------+-----------+---------
> +       1     | 1         | -          | 1      | 0         | 0
> +       1     | 1         | SHARED     | 1      | ERR       | -
> +
> +      Target is a DF_GNU_1_UNIQUE DSO:
> +      Before | Target NS | RTLD Flags | After  | handle NS | libc NS
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-unique1:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | 0         | -          | +0     | 0         | 0
> +       0     | 0         | -          | 0      | 0         | 0
> +       0     | 0         | SHARED     | 0      | 0         | 0
> +       0     | +         | -          | 0,+1p  | 1p        | 0
> +       0,1p  | 0         | -          | 0,1p   | 0         | 0
> +       0,1p  | 0         | SHARED     | 0,1p   | 0         | 0
> +       0,1p  | 1         | -          | 0,1p   | 1p        | 0
> +       0,1p  | 1         | SHARED     | 0,1p   | 1p        | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-unique2:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | 0         | SHARED     | +0     | 0         | 0
> +       0     | +         | SHARED     | 0,+1p  | 1p        | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-unique3:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | +         | -          | +0,+1p | 1p        | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-unique4:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | +         | SHARED     | +0,+1p | 1p        | 0
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-unique5:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | +         | ISOLATE    | +1     | 1         | 1
> +       1     | 0         | -          | +0,1   | 0         | 0
> +       0,1   | 0         | -          | 0,1    | 0         | 0
> +       0,1   | 0         | SHARED     | 0,1    | 0         | 0
> +       0,1   | 1         | -          | 0,1    | ERR       | -
> +      =======+===========+============+========+===========+=========
> +      dlmopen-rtld-unique6:
> +      -------+-----------+------------+--------+-----------+---------
> +       -     | +1        | ISOLATE    | +1     | 1         | 1
> +       1     | 1         | -          | 1      | ERR       | -
> +       1     | 1         | SHARED     | 1      | ERR       | -
> +       1     | 0         | SHARED     | +0,1   | 0         | 0
> +       0,1   | 1         | SHARED     | 0,1    | ERR       | -
> +*/
> +
> +#include "tst-dlmopen-common.h"
> +#include <dl-dtprocnum.h>
> +#include <link.h>
> +#include <array_length.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/test-driver.h>
> +
> +#define DSO_NORMAL "$ORIGIN/tst-dlmopen-sharedmod-norm.so"
> +#define DSO_UNIQUE "$ORIGIN/tst-dlmopen-sharedmod-uniq.so"
> +#define DSO_TESTFN "rtld_shared_testfunc"
> +#define DSO_NAMESTUB "tst-dlmopen-sharedmod-"
> +#define MAX_NS 16
> +
> +#define ERROR(test, fmt, ...) \
> +  support_print_failure_impl (__FILE__, __LINE__, "%s (%s): " fmt, \
> +                              test->name, test->desc, ##__VA_ARGS__)

I think it would be better to use the macros on support/check.h instead
of reimplement another ad-hoc one.  For the majority of tests, there is
no point in keeping testing if a flaw is found.

> +
> +typedef enum
> +  {
> +   NONE  = 0,
> +   DSO   = 1,
> +   PROXY = 2,
> +   NEW   = 4,
> +  } dso_type;
> +

Wrong identation.

> +typedef struct
> +{
> +  const char *name;
> +  const char *desc;
> +  int is_prep_stage;
> +  const char *dso_name;
> +  int failure;
> +
> +  struct
> +  {
> +    const char *dso_path;
> +    Lmid_t ns;
> +    int flags;
> +  } args;
> +
> +  dso_type preloaded[MAX_NS];
> +  dso_type loaded[MAX_NS];
> +  dso_type handle_type;
> +  Lmid_t handle_ns;
> +  Lmid_t free_ns;
> +} dlmopen_test_spec;
> +
> +typedef struct { void *handle; Lmid_t ns; } test_handle;
> +static test_handle cached_handles[32];
> +
> +static void
> +cache_test_handle (void *handle, Lmid_t ns)
> +{
> +  int i;
> +
> +  for (i = 0; i < array_length(cached_handles); i++)

Space after 'array_length'.

> +    if (cached_handles[i].handle == NULL)
> +      {
> +        cached_handles[i].handle = handle;
> +        cached_handles[i].ns = ns;
> +        break;
> +      }
> +}
> +
> +__attribute__((unused))
> +static struct link_map *
> +link_map_of_dl_handle (void *handle)
> +{
> +  struct link_map *lm = NULL;
> +
> +  if (dlinfo (handle, RTLD_DI_LINKMAP, &lm) == 0)
> +    return lm;
> +
> +  printf ("dlinfo (LINKMAP) for %s in %s failed: %s\n",
> +          LIBC_SO, __func__, dlerror ());
> +
> +  return NULL;
> +}
> +
> +__attribute__((unused))
> +static Lmid_t
> +ns_of_dl_handle (void *handle)
> +{
> +  Lmid_t ns = 0;
> +
> +  if (dlinfo (handle, RTLD_DI_LMID, &ns) == 0)
> +    return ns;
> +
> +  printf ("dlinfo (LMID) for %s in %s failed: %s\n",
> +          LIBC_SO, __func__, dlerror ());
> +
> +  return -1;
> +}
> +
> +__attribute__((unused))
> +static Lmid_t real_ns_of_dl_handle (void *handle)

Newline after return type.

> +{
> +  Lmid_t ns = 0;
> +  struct link_map *lm = link_map_of_dl_handle (handle);
> +
> +  if (lm == NULL)
> +    return -1;
> +
> +  if (lm->l_proxy)
> +    ns = ns_of_dl_handle ((void *) lm->l_real);
> +  else
> +    ns = ns_of_dl_handle (handle);
> +
> +  return ns;
> +}
> +
> +__attribute__((unused))
> +static const char *str_soname (const char *name)

Ditto.

> +{
> +  char *slash = NULL;
> +
> +  if (name == NULL)
> +    return NULL;
> +
> +  if ((slash = strrchr (name, '/')))
> +    return ++slash;
> +  else
> +    return name;
> +}
> +
> +__attribute__((unused))
> +static const char *lm_name (struct link_map *lm)

Ditto.

> +{
> +  if (lm)
> +    return lm->l_name;
> +
> +  return NULL;
> +}
> +
> +
> +static int dlm_dso_is_loaded (void *handle)

Ditto.

> +{
> +  if (handle != RTLD_DEFAULT)
> +    {
> +      Dl_info sinfo = {};
> +
> +      /* If it wasn't dynamically loaded it can be of no interest to our test(s).  */
> +      if (((struct link_map *) handle)->l_type != lt_loaded)
> +        return 0;
> +
> +      /* Skip link map entries that have not yet been promoted to dl*foo* handles.  */
> +      if (((struct link_map *) handle)->l_searchlist.r_list  == NULL ||
> +          ((struct link_map *) handle)->l_searchlist.r_nlist == 0)
> +        return 0;
> +
> +      verbose_printf ("checking %p %s for %s\n",
> +                      handle, ((struct link_map *)handle)->l_name, DSO_TESTFN);
> +
> +      void *symbol = dlsym (handle, DSO_TESTFN);
> +
> +      dladdr (symbol, &sinfo);
> +      verbose_printf ("  -> %s (in %s (%p))\n",
> +                      sinfo.dli_fname,
> +                      sinfo.dli_sname,
> +                      sinfo.dli_saddr);
> +
> +      return symbol ? 1 : 0;
> +    }
> +
> +  for (int i = 0; i < array_length(cached_handles); i++)

Ditto.

> +    {
> +      if (cached_handles[i].handle == NULL)
> +        break;
> +
> +      if (((struct link_map *) cached_handles[i].handle)->l_type != lt_loaded)
> +        continue;
> +
> +      verbose_printf ("checking %p %s for %s\n",
> +                      cached_handles[i].handle,
> +                      ((struct link_map *)cached_handles[i].handle)->l_name,
> +                      DSO_TESTFN);
> +
> +      if (dlsym (cached_handles[i].handle, DSO_TESTFN) != NULL)
> +        return 1;
> +    }
> +
> +  return 0;
> +}

> +
> +static int
> +call_testfunc (dlmopen_test_spec *test, void *handle)
> +{
> +  Dl_info dli = {};
> +  struct link_map *lm = NULL;
> +  dlmopen_testfunc func = NULL;
> +  dlmopen_testresult *result = NULL;
> +
> +  if (handle != RTLD_DEFAULT)
> +    func = dlsym (handle, DSO_TESTFN);
> +
> +  if (func == NULL)
> +    {
> +      ERROR (test, "test function %s not found\n", DSO_TESTFN);
> +      return 0;
> +    }

Use FAIL_RET here.

> +
> +  result = (func)();
> +
> +  if (result == NULL)
> +    {
> +      ERROR (test, "test function %s returned NULL\n", DSO_TESTFN);
> +      return 0;
> +    }

Ditto.

> +
> +  dladdr1 (result->free, &dli, (void **)&lm, RTLD_DL_LINKMAP);
> +
> +  if (lm == NULL)
> +    {
> +      ERROR (test, "free() implementation from test module is invalid\n");
> +      return 0;
> +    }

Ditto.

> +
> +  if (lm->l_ns != test->free_ns)
> +    {
> +      ERROR (test,
> +             "free() function from test module was from ns %d, expected %d\n",
> +             (int)lm->l_ns, (int)test->free_ns);
> +      return 0;
> +    }
> +

Ditto.

> +  printf ("%s: %s: %s in ns %d using free() from ns %d: OK\n",
> +          test->name, test->args.dso_path, DSO_TESTFN,
> +          (int)test->handle_ns, (int)lm->l_ns);
> +
> +  return 1;
> +}
> +
> +static void *link_map_snapshot_array (void *handle, void *func, Lmid_t ns, size_t *len)

Ditto.

> +{
> +  struct link_map *lm = NULL;
> +  struct link_map *start = NULL;
> +
> +  if (len != NULL)
> +    *len = 0;
> +
> +  if (handle != NULL)
> +    {
> +      dlinfo (handle, RTLD_DI_LINKMAP, &lm);

Use TEST_COMPARE.

> +    }
> +  else if (func != NULL)
> +    {
> +      Dl_info dli = {};
> +
> +      dladdr1 (func, &dli, (void **)&lm, RTLD_DL_LINKMAP);
> +    }

Ditto.

> +  else if (ns >= LM_ID_BASE)
> +    {
> +      for (int i = 0; i < array_length(cached_handles); i++)
> +        {
> +          if (cached_handles[i].handle == NULL)
> +            break;
> +
> +          if (cached_handles[i].ns != ns)
> +            continue;
> +
> +          dlinfo (cached_handles[i].handle, RTLD_DI_LINKMAP, &lm);

Ditto.

> +          break;
> +        }
> +    }
> +
> +  if (lm == NULL)
> +    return NULL;
> +
> +  start = lm;
> +
> +  while (start->l_prev)
> +    start = start->l_prev;
> +
> +  size_t lm_size = 0;
> +
> +  for (lm = start; lm; lm = lm->l_next)
> +    lm_size++;
> +
> +  struct link_map **lm_list = xcalloc (lm_size + 1, sizeof (struct link_map *));

Line too long.

> +
> +  if (len != NULL)
> +    *len = lm_size;
> +
> +  int i = 0;
> +
> +  for (lm = start; lm; lm = lm->l_next)
> +    lm_list[i++] = lm;
> +  lm_list[i] = NULL;
> +
> +  return lm_list;
> +}
> +

Ok.

> +__attribute__((unused))
> +static int search_link_map_array (struct link_map **lma, size_t len, void *handle)

Ditto.

> +{
> +  if (lma == NULL)
> +    return 0;
> +
> +  if (len == 0)
> +    return 0;
> +
> +  struct link_map *target = link_map_of_dl_handle (handle);
> +
> +  for (int i = 0; i < len; i++)
> +    {
> +      if (handle == (struct link_map *)(lma[i]))
> +        return 1;
> +
> +      if (target->l_proxy)
> +        if (target->l_real == (struct link_map *)(lma[i]))
> +          return 1;
> +    }
> +
> +  return 0;
> +}
> +
> +#ifdef DEBUG_DSO_SEARCH
> +#define TRACE2(fmt, ...) \
> +  printf ("  find-test-dso (%s @ %d): " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
> +#else
> +#define TRACE2(fmt, ...)
> +#endif

We have 'test_verbose' to handle it, so just do:

  #define TRACE2(fmt, ...) \
    if (test_verbose) \
      printf ("  find-test-dso (%s @ %d): " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

> +
> +static struct link_map *
> +find_test_dso_in_link_map_array (struct link_map **lma, size_t len)
> +{
> +  if (lma == NULL)
> +    return NULL;
> +
> +  if (len == 0)
> +    return NULL;
> +
> +  for (int i = 0; i < len; i++)
> +    {
> +      if ((lma[i] == NULL) || (lma[i]->l_name == NULL))
> +        continue;
> +
> +      TRACE2 ("%p [%d/%d] %s", lma, i, (int)len - 1,
> +              lma[i] ? (lma[i]->l_name ?: "???.so") : "NULL" );
> +
> +      if (lma[i] && lma[i]->l_name)

This check is superflous (you already both above).

> +        if (strstr (lma[i]->l_name, DSO_NAMESTUB))

No implicit checks.

> +          if (dlsym (lma[i], DSO_TESTFN) != NULL)
> +            return (struct link_map *)lma[i];
> +    }
> +
> +  return NULL;
> +}
> +
> +__attribute__((unused))
> +static void *link_map_list (void *handle, void *func, int *len, Lmid_t *ns)

Ditto.

> +{
> +  struct link_map *lm = NULL;
> +  struct link_map *start = NULL;
> +
> +  if (len)
> +    *len = 0;
> +
> +  if (handle != NULL)
> +    {
> +      dlinfo (handle, RTLD_DI_LINKMAP, &lm);
> +    }
> +  else if (func != NULL)
> +    {
> +      Dl_info dli = {};
> +
> +      dladdr1 (func, &dli, (void **)&lm, RTLD_DL_LINKMAP);
> +    }

Use TEST_VERIFY.

> +
> +  if (lm == NULL)
> +    return NULL;
> +
> +  if (ns)
> +    *ns = lm->l_ns;
> +
> +  /* Rewind to start of link map list:  */
> +  start = lm;
> +
> +  while (start->l_prev != NULL)
> +    start = start->l_prev;
> +
> +  size_t lm_size = 0;
> +
> +  for (lm = start; lm != NULL; lm = lm->l_next)
> +    lm_size++;
> +
> +  if (len != NULL)
> +    *len = lm_size;
> +
> +  return start;
> +}
> +
> +#ifdef DEBUG_DLMOPEN_TEST_WRAPPER
> +#define TRACE(fmt, ...) \
> +  printf ("%s (%s @ %d): " fmt "\n", test->name, __FILE__, __LINE__, ##__VA_ARGS__)
> +#else
> +#define TRACE(fmt, ...)
> +#endif

Ditto.

> +
> +static bool process_test_spec (dlmopen_test_spec *test)
> +{
> +  void *handle = NULL;
> +  size_t lm_before_len[MAX_NS] = { 0 };
> +  size_t lm_after_len[MAX_NS] = { 0 };
> +  struct link_map **lm_before[MAX_NS] = { NULL };
> +  struct link_map **lm_after[MAX_NS] = { NULL };
> +  struct link_map *preloads[MAX_NS] = { NULL };
> +  int want_preload = 0;
> +  int test_status = false;
> +
> +  TRACE("LD_AUDIT = %s", getenv("LD_AUDIT") ?: "-");
> +  TRACE("BEFORE SNAPSHOTS: %p", &lm_before[0]);
> +  TRACE("AFTER  SNAPSHOTS: %p", &lm_after[0]);
> +  TRACE("preloads        : %p", preloads);
> +  TRACE("setup done");
> +
> +  if (test->args.dso_path && *test->args.dso_path && !test->dso_name)
> +    test->dso_name = str_soname (test->args.dso_path);
> +
> +  TRACE("DSO short name: %s", test->dso_name);
> +
> +  /* Get the existing link map contents before the test runs:  */
> +  lm_before[0] = link_map_snapshot_array (NULL, process_test_spec,
> +                                          LM_ID_BASE, &lm_before_len[0]);
> +  for (int i = 1; i < MAX_NS; i++)
> +    lm_before[i] = link_map_snapshot_array (NULL, NULL, i, &lm_before_len[i]);
> +
> +  TRACE("link map snapshots cached");
> +
> +  for (int i = 0; i < MAX_NS; i++)
> +    {
> +      if (test->preloaded[i] & PROXY)
> +        {
> +          struct link_map **lm = lm_before[i];
> +          want_preload++;
> +
> +          if (lm != NULL)
> +            for (int j = 0; (preloads[i] == NULL) && (j < lm_before_len[i]); j++)
> +              if (dlm_dso_is_loaded (lm[j]) && lm[j]->l_proxy)
> +                preloads[i] = lm[j];
> +
> +          if (preloads[i] == NULL)
> +            {
> +              ERROR (test,
> +                     "needed proxy for %s preloaded in NS %d, not found\n",
> +                     test->dso_name, i);

Use FAIL_RET here (I see little point in cleanup resourse specially if there is a failure).

> +              goto cleanup;
> +            }
> +        }
> +      else if (test->preloaded[i] & DSO)
> +        {
> +          struct link_map **lm = lm_before[i];
> +          int lm_max = lm_before_len[i];
> +          want_preload++;
> +
> +          if (lm != NULL)
> +            for (int j = 0; !preloads[i] && (j < lm_max); j++)
> +              {
> +                if (dlm_dso_is_loaded (lm[j]) && !lm[j]->l_proxy)
> +                  preloads[i] = lm[j];
> +              }
> +          if (!preloads[i])
> +            {
> +              ERROR (test,
> +                     "needed %s preloaded in NS %d, not found\n",
> +                     test->dso_name, i);
> +              goto cleanup;
> +            }
> +        }
> +    }
> +  TRACE("preload checks (A)");
> +
> +  if (dlm_dso_is_loaded (RTLD_DEFAULT))
> +    {
> +      /* Test DSO module must _not_ be preloaded, and is:  */
> +      if (!want_preload)
> +        {
> +          ERROR (test, "DSO %s unexpectedly loaded before test\n", test->dso_name);
> +          goto cleanup;
> +        }
> +    }
> +  else
> +    {
> +      /* DSO is not loaded, and must be.  In theory we can never see
> +         this error as it should be caught by the preceding preload loop.  */
> +      if (want_preload)
> +        {
> +          ERROR (test, "DSO %s must be preloaded (and is not)\n", test->args.dso_path);
> +          goto cleanup;
> +        }
> +    }
> +  TRACE("preload checks (B) %s", test->name);
> +
> +  if (!(test->args.flags & (RTLD_NOW|RTLD_LAZY)))
> +    test->args.flags |= RTLD_NOW;
> +
> +  handle = dlmopen (test->args.ns, test->args.dso_path, test->args.flags);
> +  TRACE("dlmopen returned %p", handle);
> +
> +  if (handle == NULL)
> +    {
> +      const char *status = "failed";
> +      const char *plabel = "";
> +
> +      if (test->failure)
> +        {
> +          test_status = true;
> +
> +          printf ("%s: dlmopen(%s, %d, 0x%0x) failed: OK (EXPECTED)\n",
> +                  test->name, test->args.dso_path,
> +                  (int)test->args.ns, (int)test->args.flags);
> +          printf ("Returned: %p\n\n", handle);
> +
> +          goto cleanup;
> +        }
> +
> +      if (test->is_prep_stage)
> +        plabel = "(during setup of preconditions): ";
> +
> +      if (test->args.ns == LM_ID_BASE)
> +        ERROR (test, "%sdlmopen (LM_ID_BASE, \"%s\", 0x%x) %s: %s\n",
> +               plabel, test->args.dso_path, test->args.flags, status, dlerror ());
> +      else
> +        ERROR (test, "%sdlmopen (%d, \"%s\", 0x%x) %s: %s\n",
> +               plabel, (int)test->args.ns, test->args.dso_path, test->args.flags, status,
> +               dlerror ());
> +
> +      goto cleanup;
> +    }
> +  else if (test->failure)
> +    {
> +      ERROR (test, "dlmopen() call should have failed, but did not\n");
> +      goto cleanup;
> +    }
> +
> +  TRACE("return status checked");
> +
> +  if (!dlm_dso_is_loaded (handle))
> +    {
> +      ERROR (test, "DSO %s (%p) missing function (%s)\n",
> +             test->args.dso_path, handle, DSO_TESTFN);
> +      goto cleanup;
> +    }
> +
> +  TRACE ("loaded DSO sanity checked");
> +
> +  Lmid_t hns = ns_of_dl_handle (handle);
> +  Lmid_t real_hns = real_ns_of_dl_handle (handle);
> +  Lmid_t proxy_ns = 0;
> +
> +  call_testfunc (test, handle);
> +  TRACE (DSO_TESTFN "called");
> +
> +  cache_test_handle (handle, hns);
> +  TRACE ("handle %p cached (ns %d)", handle, (int)hns);
> +
> +  /* If the real ns was different to the apparent one
> +     then we have a proxy and we need to shuffle the values,
> +     else leave the proxy ns as 0 as an expect.proxy_ns of 0
> +     means we weren't expecting a proxy:  */
> +  if (real_hns != hns)
> +    {
> +      proxy_ns = hns;
> +      hns = real_hns;
> +    }
> +
> +  if (proxy_ns)
> +    printf ("Returned: proxy ns:%d (real ns: %d)\n\n", (int)proxy_ns, (int)hns);
> +  else
> +    printf ("Returned: dso ns:%d\n\n", (int)hns);
> +
> +  Lmid_t expected;
> +  if (test->handle_type & PROXY)
> +    expected = proxy_ns;
> +  else
> +    expected = hns;
> +
> +  TRACE("check expected ns %d", (int)expected);
> +
> +  if (test->args.ns == LM_ID_NEWLM)
> +    {
> +      if (expected <= LM_ID_BASE)
> +        {
> +          ERROR (test, "DSO should have been in NS > %d, was in %d\n",
> +                 LM_ID_BASE, (int)expected);
> +          goto cleanup;
> +        }
> +
> +      /* For any cases where we can't predict the namespace in advance:  */
> +      if (test->handle_ns == LM_ID_NEWLM)
> +        test->handle_ns = expected;
> +    }
> +  else
> +    {
> +      if (test->args.ns != expected)
> +        {
> +          ERROR (test, "DSO should have been in NS %d, was in %d\n",
> +                 (int)test->args.ns, (int)expected);
> +          goto cleanup;
> +        }
> +    }
> +
> +  TRACE("ns %d Ok", (int)expected);
> +
> +if (test->handle_type & PROXY) /* Expecting a proxy.  */
> +    {
> +      if (proxy_ns != 0) /* Got a proxy.  */
> +        {
> +          if (test->handle_ns != proxy_ns) /* But not in the right place.  */
> +            {
> +              ERROR (test, "DSO proxy should have been in ns %d, was in %d\n",
> +                     (int)test->handle_ns, (int)proxy_ns);
> +              goto cleanup;
> +            }
> +        }
> +      else /* Didn't get a proxy.  */
> +        {
> +          ERROR (test,
> +                 "DSO should have been a proxy in ns %d,"
> +                 " was a non-proxy in ns %d\n",
> +                 (int)test->handle_ns, (int)hns);
> +          goto cleanup;
> +        }
> +    }
> + else /* Not expecting a proxy.  */
> +    {
> +      if (proxy_ns > 0)
> +        {
> +          ERROR (test,
> +                 "DSO should NOT have been a proxy,"
> +                 " was a proxy in ns %d (real ns %d)\n",
> +                 (int)proxy_ns, (int)hns);
> +          goto cleanup;
> +        }
> +
> +      if (test->handle_ns != hns)
> +        {
> +          ERROR (test,
> +                 "DSO should have been in ns %d,"
> +                 " was in ns %d\n",
> +                 (int)test->handle_ns, (int)hns);
> +          goto cleanup;
> +        }
> +    }
> +  TRACE ("proxy status Ok");
> +
> +  /* Get the new link map contents after the test has run:  */
> +  lm_after[0] = link_map_snapshot_array (NULL, process_test_spec,
> +                                         LM_ID_BASE, &lm_after_len[0]);
> +  for (int i = 1; i < MAX_NS; i++)
> +    lm_after[i] = link_map_snapshot_array (NULL, NULL, i, &lm_after_len[i]);
> +
> +  for (int i = 0; i < MAX_NS; i++)
> +    {
> +      TRACE("checking status of NS %d", i);
> +      void *old_handle =
> +        find_test_dso_in_link_map_array (lm_before[i], lm_before_len[i]);
> +      TRACE ("old handle is %p", old_handle);
> +
> +      void *new_handle =
> +        find_test_dso_in_link_map_array (lm_after[i], lm_after_len[i]);
> +      TRACE ("new handle is %p", new_handle);
> +
> +      if (test->loaded[i] == NONE)
> +        {
> +          TRACE ("ns %d requirement is NONE", i);
> +          if (old_handle != NULL)
> +            {
> +              ERROR (test,
> +                     "Unexpected preload DSO %s in ns %d\n",
> +                     lm_name (old_handle), i);
> +              goto cleanup;
> +            }
> +          if (new_handle != NULL)
> +            {
> +              ERROR (test, "Unexpected new DSO %s in ns %d\n",
> +                     lm_name (new_handle), i);
> +              goto cleanup;
> +            }
> +          continue;
> +        }
> +
> +      if (test->loaded[i] & NEW)
> +        {
> +          TRACE("ns %d requirement is NEW", i);
> +          if (old_handle != NULL)
> +            {
> +              ERROR (test,
> +                     "DSO in ns %d should have been a new load,"
> +                     " found to have been preloaded\n", i);
> +              goto cleanup;
> +            }
> +          if (new_handle == NULL)
> +            {
> +              ERROR (test, "Expected DSO in ns %d, not found\n", i);
> +              goto cleanup;
> +            }
> +        }
> +      else
> +        {
> +          TRACE("ns %d requirement is OLD", i);
> +          if (new_handle == NULL)
> +            {
> +              ERROR (test, "Expected new DSO in ns %d, not found\n", i);
> +              goto cleanup;
> +            }
> +
> +          if (old_handle != new_handle)
> +            {
> +              ERROR (test, "DSO in ns %d changed. This should be impossible, "
> +                     "sanity check the test code\n", i);
> +              goto cleanup;
> +            }
> +        }
> +
> +      if (test->loaded[i] & PROXY)
> +        {
> +          TRACE ("rechecking DSO status in ns %d", i);
> +          if (!((struct link_map *)new_handle)->l_proxy)
> +            {
> +              ERROR (test, "DSO in ns %d should be a proxy but is not\n", i);
> +              goto cleanup;
> +            }
> +        }
> +      else
> +        {
> +          TRACE ("rechecking proxy status in ns %d", i);
> +          if (((struct link_map *)new_handle)->l_proxy)
> +            {
> +              ERROR (test, "DSO in ns %d should NOT be a proxy but is\n", i);
> +              goto cleanup;
> +            }
> +        }
> +    }
> +
> +  test_status = true;
> +
> + cleanup:
> +  for (int i = 0; i < MAX_NS; i++)
> +     free (lm_after[i]);
> +  for (int i = 0; i < MAX_NS; i++)
> +    free (lm_before[i]);
> +
> +  return test_status;
> +}

Same remarks ad before for TRACE and ERROR macros.

> diff --git a/elf/tst-dlmopen-modules.h b/elf/tst-dlmopen-modules.h
> new file mode 100644
> index 0000000000..63aa7730e3
> --- /dev/null
> +++ b/elf/tst-dlmopen-modules.h
> @@ -0,0 +1,21 @@
> +/* Module-specific infrastructure for tst-dlmopen-rtld-*
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#pragma once

No pragma once.

> +
> +#include "tst-dlmopen-common.h"

This whole header is unecessary.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared1.c b/elf/tst-dlmopen-rtld-audit-shared1.c
> new file mode 100644
> index 0000000000..f61e00d372
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared1.c
> @@ -0,0 +1,8 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared1.h"
> +
> +#include "tst-dlmopen-std-do-test.c"
> +

No newline at the end of the file.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared2.c b/elf/tst-dlmopen-rtld-audit-shared2.c
> new file mode 100644
> index 0000000000..93d7c9d502
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared2.c
> @@ -0,0 +1,8 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared2.h"
> +
> +#include "tst-dlmopen-std-do-test.c"
> +

Ditto.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared3.c b/elf/tst-dlmopen-rtld-audit-shared3.c
> new file mode 100644
> index 0000000000..bc1e8f0345
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared3.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared3.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared4.c b/elf/tst-dlmopen-rtld-audit-shared4.c
> new file mode 100644
> index 0000000000..0a0ecd4317
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared4.c
> @@ -0,0 +1,8 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared4.h"
> +
> +#include "tst-dlmopen-std-do-test.c"
> +

Ditto.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared5.c b/elf/tst-dlmopen-rtld-audit-shared5.c
> new file mode 100644
> index 0000000000..1585e48a95
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared5.c
> @@ -0,0 +1,8 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared5.h"
> +
> +#include "tst-dlmopen-std-do-test.c"
> +
> diff --git a/elf/tst-dlmopen-rtld-audit-shared6.c b/elf/tst-dlmopen-rtld-audit-shared6.c
> new file mode 100644
> index 0000000000..f1b21c295b
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared6.c
> @@ -0,0 +1,8 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared6.h"
> +
> +#include "tst-dlmopen-std-do-test.c"
> +

Ditto.

> diff --git a/elf/tst-dlmopen-rtld-audit-unique1.c b/elf/tst-dlmopen-rtld-audit-unique1.c
> new file mode 100644
> index 0000000000..5605967a5c
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique1.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique1.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-unique2.c b/elf/tst-dlmopen-rtld-audit-unique2.c
> new file mode 100644
> index 0000000000..8a1d5317c8
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique2.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique2.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-unique3.c b/elf/tst-dlmopen-rtld-audit-unique3.c
> new file mode 100644
> index 0000000000..31848ea9d8
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique3.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique3.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-unique4.c b/elf/tst-dlmopen-rtld-audit-unique4.c
> new file mode 100644
> index 0000000000..c79cb378d5
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique4.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique4.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-unique5.c b/elf/tst-dlmopen-rtld-audit-unique5.c
> new file mode 100644
> index 0000000000..790fed9b04
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique5.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique5.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-unique6.c b/elf/tst-dlmopen-rtld-audit-unique6.c
> new file mode 100644
> index 0000000000..0b9bbabdd2
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique6.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique6.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared1.c b/elf/tst-dlmopen-rtld-shared1.c
> new file mode 100644
> index 0000000000..329a9a91d9
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared1.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared1.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared1.h b/elf/tst-dlmopen-rtld-shared1.h
> new file mode 100644
> index 0000000000..de718fd8ca
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared1.h
> @@ -0,0 +1,64 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen:0:none--ns0",
> +    .desc = "dlmopen as dlopen",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .loaded = { [0] = DSO|NEW },
> +    .handle_type = DSO,
> +    .handle_ns = LM_ID_BASE,
> +   },
> +   {
> +    .name = "dlmopen:0:ns0--ns0",
> +    .desc = "dlmopen a preloaded DSO in the base NS",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO },
> +    .handle_type = DSO,
> +    .handle_ns = LM_ID_BASE,
> +   },
> +   {
> +    .name = "dlmopen-shared:0:ns0--ns0",
> +    .desc = "dlmopen a preloaded DSO in the base NS with RTLD_SHARED",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO },
> +    .handle_type = DSO,
> +    .handle_ns = LM_ID_BASE,
> +   },
> +   {
> +    .name = "dlmopen:0:ns0--nsX",
> +    .desc = "dlmopen a preloaded DSO in the base NS into a new NS",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_NEWLM,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO|NEW },
> +    .handle_type = DSO,
> +    .handle_ns = EXPECTED_NS,
> +   },
> +   {
> +    .name = "dlmopen:0:ns0-nsX--ns0-nsX",
> +    .desc = "dlmopen a preloaded DSO in the base & secondary NS into the base NS",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .handle_type = DSO,
> +    .handle_ns = LM_ID_BASE,
> +   },
> +   {
> +    .name = "dlmopen-shared:0:ns0-nsX--ns0-nsX",
> +    .desc = "dlmopen a preloaded DSO in the base & secondary NS into the base NS",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .handle_type = DSO,
> +    .handle_ns = LM_ID_BASE,
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared2.c b/elf/tst-dlmopen-rtld-shared2.c
> new file mode 100644
> index 0000000000..b5bfe047e5
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared2.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared2.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared2.h b/elf/tst-dlmopen-rtld-shared2.h
> new file mode 100644
> index 0000000000..779ce62dfb
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared2.h
> @@ -0,0 +1,66 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-shared:0:none--ns0",
> +    .desc = "dlmopen as dlopen with RTLD_SHARED",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags  = RTLD_SHARED,
> +    .loaded = { [0] = DSO|NEW },
> +    .handle_ns = LM_ID_BASE,
> +    .handle_type = DSO,
> +   },
> +   {
> +    .name = "dlmopen-shared:X:ns0--ns0-nsXp",
> +    .desc = "dlmopen into a new namespace with the target already in the base NS",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_NEWLM,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY|NEW }
> +   },
> +   {
> +    .name = "dlmopen:0:ns0-nsXp--ns0-nsXp",
> +    .desc = "dlmopen into base NS while proxy already in nsX",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +   {
> +    .name = "dlmopen-shared:0:ns0-nsXp--ns0-nsXp",
> +    .desc = "dlmopen with RTLD_SHARED into base NS while proxy already in nsX",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +   {
> +    .name = "dlmopen:X:ns0-nsXp--ns0-nsXp",
> +    .desc = "dlmopen into NS X while proxy already in nsX",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = EXPECTED_NS,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +   {
> +    .name = "dlmopen-shared:X:ns0-nsXp--ns0-nsXp",
> +    .desc = "dlmopen with RTLD_SHARED into NS X while proxy already in nsX",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = EXPECTED_NS,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared3.c b/elf/tst-dlmopen-rtld-shared3.c
> new file mode 100644
> index 0000000000..f1a4c0fb8a
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared3.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared3.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared3.h b/elf/tst-dlmopen-rtld-shared3.h
> new file mode 100644
> index 0000000000..894626b1f4
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared3.h
> @@ -0,0 +1,43 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen:X:none--nsX",
> +    .desc = "dlmopen into nsX, no copies preloaded",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_NEWLM,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { },
> +    .loaded = { [EXPECTED_NS] = DSO|NEW },
> +   },
> +   {
> +    .name = "dlmopen:0:nsX--ns0-nsX",
> +    .desc = "dlmopen into ns 0, copy already loaded in ns X",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen:X:ns0-nsX--nsX",
> +    .desc = "dlmopen into ns X, copies already in ns 0 and ns X",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = EXPECTED_NS,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-shared:X:ns0-nsX--nsX",
> +    .desc = "dlmopen RTLD_SHARED into nsX with a DSO already in NS0 and NSX",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = EXPECTED_NS,
> +    .args.flags = RTLD_SHARED,
> +    .failure = 1,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared4.c b/elf/tst-dlmopen-rtld-shared4.c
> new file mode 100644
> index 0000000000..395ced83c2
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared4.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared4.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared4.h b/elf/tst-dlmopen-rtld-shared4.h
> new file mode 100644
> index 0000000000..9b4d0f06e0
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared4.h
> @@ -0,0 +1,14 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-shared:X:none--ns0-nsX",
> +    .desc = "dlmopen a new proxy in nsX with no preexisting dso in ns0",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_NEWLM,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { },
> +    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = PROXY|NEW },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared5.c b/elf/tst-dlmopen-rtld-shared5.c
> new file mode 100644
> index 0000000000..fe830b4162
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared5.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared5.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared5.h b/elf/tst-dlmopen-rtld-shared5.h
> new file mode 100644
> index 0000000000..13dca63cbd
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared5.h
> @@ -0,0 +1,25 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-preload:X:none--nsX",
> +    .desc = "preload a DSO into ns1 to prepare for other tests",
> +    .is_prep_stage = 1,
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_NEWLM,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { },
> +    .loaded = { [EXPECTED_NS] = DSO|NEW },
> +   },
> +   {
> +    .name = "dlmopen-shared:0:nsX--nsX-ns0",
> +    .desc = "dlmopen RTLD_SHARED into ns0 when preloaded into nsX",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
> +   },
> +  };

Ok.


> diff --git a/elf/tst-dlmopen-rtld-shared6.c b/elf/tst-dlmopen-rtld-shared6.c
> new file mode 100644
> index 0000000000..989a37d78a
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared6.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared6.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared6.h b/elf/tst-dlmopen-rtld-shared6.h
> new file mode 100644
> index 0000000000..de41333619
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared6.h
> @@ -0,0 +1,36 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-preload:X:none--nsX",
> +    .desc = "preload a DSO into nsX to prepare for other tests",
> +    .is_prep_stage = 1,
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = LM_ID_NEWLM,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { },
> +    .loaded = { [EXPECTED_NS] = DSO|NEW },
> +   },
> +   {
> +    .name = "dlmopen:X:nsX--nsX",
> +    .desc = "dlmopen a dso in nsX while already loaded there",
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = EXPECTED_NS,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +    .loaded = { [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-shared:X:nsX--nsX",
> +    .desc = "dlmopen RTLD_SHARED a dso in nsX while already loaded there",
> +    .failure = 1,
> +    .args.dso_path = DSO_NORMAL,
> +    .args.ns = EXPECTED_NS,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +    .loaded = { [EXPECTED_NS] = DSO },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique1.c b/elf/tst-dlmopen-rtld-unique1.c
> new file mode 100644
> index 0000000000..f2240b5cd9
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique1.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique1.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique1.h b/elf/tst-dlmopen-rtld-unique1.h
> new file mode 100644
> index 0000000000..4b7390f772
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique1.h
> @@ -0,0 +1,86 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-unique:0:none--ns0",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into ns0",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { },
> +    .loaded = { [0] = DSO|NEW },
> +   },
> +   {
> +    .name = "dlmopen-unique:0:ns0--ns0",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into ns0 while already present",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-unique-shared:0:ns0--ns0",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns0 while already present",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-unique:X:ns0--nsX",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into nsX while present in ns0",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_NEWLM,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY|NEW },
> +   },
> +   {
> +    .name = "dlmopen-unique:0:ns0-nsXp--ns0--nsXp",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso already in ns0 proxied in nsX",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +   {
> +    .name = "dlmopen-shared-unique:0:ns0-nsXp--ns0-nsXp",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns0 already in ns0 and proxied in nsX",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +   {
> +    .name = "dlmopen-unique:0:ns0-nsXp--ns0-nsXp",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into ns0 already in ns0 and proxied in nsX",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = EXPECTED_NS,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +   {
> +    .name = "dlmopen-unique:0:ns0-nsXp--ns0-nsXp",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns0 already in ns0 and proxied in nsX",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = EXPECTED_NS,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique2.c b/elf/tst-dlmopen-rtld-unique2.c
> new file mode 100644
> index 0000000000..5dd8c2678d
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique2.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique2.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique2.h b/elf/tst-dlmopen-rtld-unique2.h
> new file mode 100644
> index 0000000000..bf6871ab58
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique2.h
> @@ -0,0 +1,25 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-shared-unique:0:none--ns0",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso in the base ns",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { },
> +    .loaded = { [0] = DSO|NEW },
> +   },
> +   {
> +    .name = "dlmopen-shared-unique:1:ns0--ns0-ns1p",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into ns1 while present in ns0",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_NEWLM,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { [0] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = PROXY|NEW },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique3.c b/elf/tst-dlmopen-rtld-unique3.c
> new file mode 100644
> index 0000000000..237b3adcc1
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique3.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique3.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique3.h b/elf/tst-dlmopen-rtld-unique3.h
> new file mode 100644
> index 0000000000..139ade0d28
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique3.h
> @@ -0,0 +1,13 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-unique:X:none--ns0-ns1p",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into nsX",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_NEWLM,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { },
> +    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = PROXY|NEW },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique4.c b/elf/tst-dlmopen-rtld-unique4.c
> new file mode 100644
> index 0000000000..e13ca19fc7
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique4.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique4.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique4.h b/elf/tst-dlmopen-rtld-unique4.h
> new file mode 100644
> index 0000000000..1a9b93c2ac
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique4.h
> @@ -0,0 +1,14 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-shared-unique:X:none--ns0-nsXp",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_NEWLM,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = PROXY,
> +    .preloaded = { },
> +    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = PROXY|NEW },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique5.c b/elf/tst-dlmopen-rtld-unique5.c
> new file mode 100644
> index 0000000000..014b10e45e
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique5.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique5.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique5.h b/elf/tst-dlmopen-rtld-unique5.h
> new file mode 100644
> index 0000000000..75dca65821
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique5.h
> @@ -0,0 +1,58 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-isolate-unique:X:none--nsX",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into NSX with RTLD_ISOLATE",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_NEWLM,
> +    .args.flags = RTLD_ISOLATE,
> +    .handle_ns = EXPECTED_NS,
> +    .free_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { },
> +    .loaded = { [EXPECTED_NS] = DSO|NEW },
> +   },
> +   {
> +    .name = "dlmopen-unique:0:nsX--ns0-nsX",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso already present in NS X",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-unique:0:ns0-nsX--ns0-nsX",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso already in the base NS and NS X",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-shared-unique:0:ns0-nsX--ns0-nsX",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already in the base NS and NS X",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-shared-unique:X:ns0-nsX--ns0-nsX",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already in the base NS and NS X into NS X",
> +    .failure = 1,
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = EXPECTED_NS,
> +    .args.flags = RTLD_SHARED,
> +    .handle_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique6.c b/elf/tst-dlmopen-rtld-unique6.c
> new file mode 100644
> index 0000000000..c0df189d3f
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique6.c
> @@ -0,0 +1,7 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique6.h"
> +
> +#include "tst-dlmopen-std-do-test.c"

Ok.

> diff --git a/elf/tst-dlmopen-rtld-unique6.h b/elf/tst-dlmopen-rtld-unique6.h
> new file mode 100644
> index 0000000000..062f1f2277
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique6.h
> @@ -0,0 +1,51 @@
> +static dlmopen_test_spec dltest[] =
> +  {
> +   {
> +    .name = "dlmopen-isolate-unique:1:none--ns1--prep",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into NS1 with RTLD_ISOLATE",
> +    .is_prep_stage = 1,
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_NEWLM,
> +    .args.flags = RTLD_ISOLATE,
> +    .handle_ns = EXPECTED_NS,
> +    .free_ns = EXPECTED_NS,
> +    .handle_type = DSO,
> +    .preloaded = { },
> +    .loaded = { [EXPECTED_NS] = DSO|NEW },
> +   },
> +   {
> +    .name = "dlmopen-unique:1:nsX--nsX--FAIL",
> +    .desc = "dlmopen a DF_GNU_1_UNIQUE dso into NSX when already there",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = EXPECTED_NS,
> +    .failure = 1,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-shared-unique:X:nsX--nsX--FAIL",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso into NSX when already there",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = EXPECTED_NS,
> +    .args.flags = RTLD_SHARED,
> +    .failure = 1,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-shared-unique:0:nsX--ns0-nsX",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already present in NS X",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = LM_ID_BASE,
> +    .handle_ns = 0,
> +    .handle_type = DSO,
> +    .preloaded = { [EXPECTED_NS] = DSO },
> +    .loaded = { [0] = DSO|NEW, [EXPECTED_NS] = DSO },
> +   },
> +   {
> +    .name = "dlmopen-shared-unique:X:ns0-nsX--ns0-nsX--FAIL",
> +    .desc = "dlmopen RTLD_SHARED a DF_GNU_1_UNIQUE dso already in the base NS and NS 1",
> +    .args.dso_path = DSO_UNIQUE,
> +    .args.ns = EXPECTED_NS,
> +    .failure = 1,
> +    .preloaded = { [0] = DSO, [EXPECTED_NS] = DSO },
> +   },
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-sharedmod-norm.c b/elf/tst-dlmopen-sharedmod-norm.c
> new file mode 100644
> index 0000000000..915a256cd9
> --- /dev/null
> +++ b/elf/tst-dlmopen-sharedmod-norm.c
> @@ -0,0 +1,34 @@
> +#include "tst-dlmopen-modules.h"
> +#include <signal.h>
> +#include <time.h>
> +#include <stdlib.h>
> +
> +static char user_name[255];
> +
> +/* This initialiser checks that relocation from DF_GNU_1_UNIQUE
> +   dependencies (via DT_NEEDED) work properly - it will segfault
> +   if _dl_lookup_symbol_x isn't handling that case. The main test
> +   framework for dlmopen checks relocations/lookups via dlmopen.  */
> +void __attribute__ ((constructor)) check_relocation (void)

Space after the attribute.

> +{
> +  char *user = getenv("USER");

Space after function name.

> +
> +  if (user == NULL)
> +    return;
> +
> +  for (int i = 0; i < sizeof (user_name) && *(user + i); i++)
> +    user_name[i] = *(user + i);
> +
> +  user[sizeof (user_name) - 1] = 0;
> +}
> +
> +dlmopen_testresult *rtld_shared_testfunc (void)
> +{
> +  static dlmopen_testresult result;
> +
> +  result.name = "norm";
> +  result.free = free;
> +  result.timer_create = timer_create;
> +
> +  return &result;
> +}

Ok.

> diff --git a/elf/tst-dlmopen-sharedmod-uniq.c b/elf/tst-dlmopen-sharedmod-uniq.c
> new file mode 100644
> index 0000000000..ec646c0d15
> --- /dev/null
> +++ b/elf/tst-dlmopen-sharedmod-uniq.c
> @@ -0,0 +1,33 @@
> +#include "tst-dlmopen-modules.h"
> +#include <signal.h>
> +#include <time.h>
> +
> +static char user_name[255];
> +
> +/* This initialiser checks that relocation from DF_GNU_1_UNIQUE
> +   dependencies (via DT_NEEDED) work properly - it will segfault
> +   if _dl_lookup_symbol_x isn't handling that case. The main test
> +   framework for dlmopen checks relocations/lookups via dlmopen.  */
> +void __attribute__ ((constructor)) check_relocation (void)

Ditto.

> +{
> +  char *user = getenv("USER");
> +
> +  if (user == NULL)
> +    return;
> +
> +  for (int i = 0; i < sizeof (user_name) && *(user + i); i++)
> +    user_name[i] = *(user + i);
> +
> +  user[sizeof (user_name) - 1] = 0;
> +}
> +
> +dlmopen_testresult *rtld_shared_testfunc (void)
> +{
> +  static dlmopen_testresult result;
> +
> +  result.name = "noop";
> +  result.free = free;
> +  result.timer_create = timer_create;
> +
> +  return &result;
> +}

Ok.

> diff --git a/elf/tst-dlmopen-std-do-test.c b/elf/tst-dlmopen-std-do-test.c
> new file mode 100644
> index 0000000000..9f1d3a3767
> --- /dev/null
> +++ b/elf/tst-dlmopen-std-do-test.c
> @@ -0,0 +1,12 @@
> +#include <array_length.h>
> +
> +static int
> +do_test (void)
> +{
> +  for (int i = 0; i < array_length (dltest); i++)
> +    if (!process_test_spec (&dltest[i]))
> +      return 1;
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> 

Ok.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO
  2021-07-08 16:32 ` [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO Vivek Das Mohapatra via Libc-alpha
@ 2021-08-09 20:10   ` Adhemerval Zanella via Libc-alpha
  2021-08-09 21:24   ` Florian Weimer via Libc-alpha
  1 sibling, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 20:10 UTC (permalink / raw)
  To: libc-alpha, Vivek Das Mohapatra



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> libpthread should have no contents that are sensitive
> to duplication anymore, so doesn't require special
> treatment under dlmopen.

LGTM, but this patch should be merged with the one that actually adds
actually handles DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE.

> ---
>  extra-lib.mk  | 2 ++
>  htl/Makefile  | 3 ++-
>  nptl/Makefile | 3 ++-
>  3 files changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/extra-lib.mk b/extra-lib.mk
> index 9051958ec0..38184adae1 100644
> --- a/extra-lib.mk
> +++ b/extra-lib.mk
> @@ -102,9 +102,11 @@ $(objpfx)$(lib).so: $(firstword $($(lib)-map) \
>  					    $(filter $(lib).map, \
>  						     $(version-maps))))
>  ifneq ($(ld-zunique),yes)
> +ifneq ($(lib),libpthread)
>  $(objpfx)$(lib).so: $(common-objpfx)/elf/dynamic-notes.os
>  endif
>  endif
> +endif
>  
>  endif
>  
> diff --git a/htl/Makefile b/htl/Makefile
> index c2a25dcd79..ce6d9a8cd1 100644
> --- a/htl/Makefile
> +++ b/htl/Makefile
> @@ -202,9 +202,10 @@ $(inst_libdir)/libpthread_syms.a: $(srcdir)/libpthread_syms.a $(+force)
>  libc-link.so = $(common-objpfx)libc.so
>  
>  extra-B-pthread.so = -B$(common-objpfx)htl/
> +# Turn off DF_GNU_1_UNIQUE for libpthread now that it's a stub.
>  LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
>  ifeq ($(ld-zunique),yes)
> -LDFLAGS-pthread.so += -Wl,-z,unique
> +LDFLAGS-pthread.so += -Wl,-z,nounique
>  endif
>  
>  include ../Rules
> diff --git a/nptl/Makefile b/nptl/Makefile
> index 81353b9455..7fcddbb263 100644
> --- a/nptl/Makefile
> +++ b/nptl/Makefile
> @@ -376,8 +376,9 @@ CPPFLAGS-tst-pthread-gdb-attach-static.c := \
>  tst-pthread-gdb-attach-no-pie = yes
>  
>  LDFLAGS-pthread.so = -Wl,--enable-new-dtags,-z,nodelete,-z,initfirst
> +# Turn off DF_GNU_1_UNIQUE for libpthread now that it's a stub.
>  ifeq ($(ld-zunique),yes)
> -LDFLAGS-pthread.so += -Wl,-z,unique
> +LDFLAGS-pthread.so += -Wl,-z,nounique
>  endif
>  
>  tests += tst-cancelx7 tst-cancelx17 tst-cleanupx4
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag (bug 22745)
  2021-08-09 19:07   ` Adhemerval Zanella via Libc-alpha
@ 2021-08-09 20:24     ` Adhemerval Zanella via Libc-alpha
  0 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 20:24 UTC (permalink / raw)
  To: Vivek Das Mohapatra, libc-alpha

> On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
>> +/* Proxy an existing link map entry into a new link map:
>> +   This is based on _dl_new_object, skipping the steps we know we won't need
>> +   because this is mostly just a shell for the l_real pointer holding the real
>> +   link map entry (normally l == l->l_real, but not for ld.so in non-main
>> +   link maps or RTLD_SHARED proxies).
>> +   It also flags the proxy by setting l_proxy, and sets the the no-delete
>> +   flag in the original if it is an lt_loaded.  */
>> +struct link_map *
>> +_dl_new_proxy (struct link_map *old, int mode, Lmid_t nsid)
>> +{
>> +  const char *name;
>> +  struct link_map *new;
>> +  struct libname_list *newname;
>> +#ifdef SHARED
>> +  unsigned int na = GLRO(dl_naudit);
>> +
>> +  if ((mode & __RTLD_OPENEXEC) != 0)
>> +    na = DL_NNS;
>> +
>> +  size_t audit_space = na * sizeof (struct auditstate);
>> +#else
>> +# define audit_space 0
>> +#endif
>> +
>> +  name = old->l_name;
>> +
>> +  /* Find the original link map entry if 'old' is itself a proxy. */
>> +  while (old != NULL && old->l_proxy)
>> +    old = old->l_real;
>> +
>> +  if (old == NULL)
>> +    _dl_signal_error (EINVAL, name, NULL, N_("cannot proxy NULL link_map"));
>> +
>> +  /* Object already exists in the target namespace.  This should get handled
>> +     by dl_open_worker but just in case we get this far, handle it:  */
>> +  if (__glibc_unlikely (old->l_ns == nsid))
>> +    _dl_signal_error (EEXIST, name, NULL,
>> +                      N_("existing object cannot be demoted to a proxy"));
>> +
>> +  /* Now duplicate as little of _dl_new_object as possible to get a
>> +     working proxied object in the target link map.  */
>> +  new = (struct link_map *) calloc (sizeof (*new) + audit_space
>> +                                    + sizeof (struct link_map *)
>> +                                    + sizeof (*newname) + PATH_MAX, 1);

Hurd does not support PATH_MAX and the proxy l_libname points to the
old->l_libname.  So I think there is no need to allocate the size
for the proxy name here.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag (bug 22745)
  2021-07-08 16:32 ` [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag " Vivek Das Mohapatra via Libc-alpha
  2021-08-09 19:07   ` Adhemerval Zanella via Libc-alpha
@ 2021-08-09 20:30   ` Adhemerval Zanella via Libc-alpha
  1 sibling, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 20:30 UTC (permalink / raw)
  To: libc-alpha



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> This flag will instruct dlmopen to create a shared object present
> in the main namespace and accessible from the selected namespace
> when supplied in the MODE argument.
> 
> include/link.h: Update the link_map struct to allow proxies
> 
> We already have an l_real pointer, used for a similar purpose by
> the linker for copies of ld.so in secondary namespaces. Update its
> documentation and add a bitfield to indicate when link_map entry
> is a proxy.
> 
> elf/dl-object.c: Implement a helper function to proxy link_map entries
> 
> Provides the minimal functionality needed to take an existing
> link_map entry and create a proxy for it in the specified namespace.
> 
> elf/dl-load.c, elf/dl-open.c: Implement RTLD_SHARED dlmopen proxying
> 
> This uses the new infrastructure to implement RTLD_SHARED object
> proxying via dlmopen: Instead of opening the specified object in
> the requested namespace we open it in the main namespace (if it
> is not already present there) and proxy it to the destination.
> 
> The following rules apply:
> 
> If a proxy of the object is already present in the requested namespace,
> we simply return it (with an incremented direct-open count).
> 
> If the object is already present in the requested namespace, a dl
> error is signalled, since we cannot satisfy the user's request.
> 
> Proxies are never created in the main namespace: RTLD_SHARED has no
> effect when the requested namespace is LM_ID_BASE.
> 
> elf/dl-fini.c: Handle proxy link_maps in the shutdown path (bug 22745)
> 
> When cleaning up before exit we should not call destructors or
> otherwise free [most of] the contents of proxied link_map entries
> since they share [most of] their contents with the LM_ID_BASE
> objects to which they point.
> 
> elf/dl-init.c: Skip proxy link_map entries in dl init path
> 
> Proxies should not trigger calls to DT_INIT constructors since they're
> just shims that point to the real, already loaded and initialised, objects.
> 
> elf/dl-open.c: Skip libc init if namespace has no libc map
> 
> Secondary namespaces which share their libc mapping with the main
> namespace cannot (and should not) have _dl_call_libc_early_init
> called for them by dl_open_worker.
> 
> elf/dl-open.c: When creating a proxy check NS 0 libc map
> 
> The libc_already_loaded check normally considers the libc_map entry
> in GL(dl_ns)[args->nsid].libc_map.
> 
> This is not correct for proxies, which use the libc_map from
> the default namespace (as proxies are dummy entries that point
> to the base namespace via their l_real members).
> 
> elf/dl-load.c, dl-open.c: Compare DSOs by file ID & check DF_GNU_1_UNIQUE
> 
> If _dl_map_object_from_fd finds that a DSO it was asked to
> load into a non-base namespace is already loaded (into the
> main namespace) and is flagged DF_GNU_1_UNIQUE then it should
> return that DSO's link map entry.
> 
> In such cases _dl_open_worker must notice that this has
> happened and continue down the link map proxy generation
> path instead of normal link map entry preparation.
> 
> elf/dl-load.c: error if RTLD_SHARED = DF_GNU_1_UNIQUE semantics violated
> 
> elf/dl-open.c: Use search helper to find preloaded DT_GNU_UNIQUE DSOs
> 
> If a DSO already exists (with the same name) in the base namespace
> and it is flagged DT_GNU_UNIQUE then we should behave as if a proxy
> had been requested.
> 
> elf/dl-load.c: When loading DSOs in other namespaces check DT_GNU_UNIQUE
> 
> If a DSO has not already been loaded and the target is not the main
> namespace then we must check to see if it's been DT_GNU_UNIQUE tagged
> and load it into the main namespace instead.
> 
> dl_open_worker has alread been modified to notice the discrepancy
> between the request and the result in such cases, and will set up
> a proxy in the target namespace.
> 
> elf/dl-load.c: Suppress audit calls when a (new) namespace is empty
> 
> When preparing an RTLD_SHARED proxy in a new namespace
> it is possible for the target namespace to be empty:
> 
> This can happen for RTLD_SHARED + LM_ID_NEWLM.
> 
> The audit infrastructure should not be invoked at this
> point (as there's nothing there to audit yet).
> 
> bits/dlfcn.h, elf/dl-load.c, elf/dl-open.c, elf/rtld.c,
> sysdeps/mips/bits/dlfcn.h:
> Suppress inter-namespace DSO sharing for audit libraries
> 
> Audit libraries should not participate in DSO sharing: In
> particular libraries tagged with DF_GNU_1_UNIQUE should not
> be shared between the audit namespace and any others - they
> should get their own copy.
> 
> This is signalled to the loader code by passing the RTLD_ISOLATE
> flag from the relevant entry point in the dl modes argument.
> 
> elf/dl-sym.c: dlsym, dlvsym must be able to resolve symbols via proxies

This new features should also have a NEWS entry.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen
  2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
                   ` (8 preceding siblings ...)
  2021-07-13 13:08 ` [External] : [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Alfonso Alfonso Peterssen via Libc-alpha
@ 2021-08-09 20:34 ` Adhemerval Zanella via Libc-alpha
  9 siblings, 0 replies; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-09 20:34 UTC (permalink / raw)
  To: Vivek Das Mohapatra, libc-alpha



On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> This is a revision of a previous patchset that I posted here
> regarding https://sourceware.org/bugzilla/show_bug.cgi?id=22745 
> 
> Introduction:
> 
> =======================================================================
>   As discussed in the URL above dlmopen requires a mechanism for
>   [optionally] sharing some objects between more than one namespace.
> 
>   The following patchset provides an implementation for this: If an
>   object is loaded with the new RTLD_SHARED flag we instead ensure
>   that a "master" copy exists (and is flagged as no-delete) in the
>   main namespace and a thin wrapper or clone is placed in the target
>   namespace.
> 
>   This patch series should address all the comments received on the
>   earlier (v1-v10) series.
> 
> =======================================================================
> 
> Changes from v11:
> 
>  - If a DSO is required in a non-base namespace because it is mentioned
>    in a DT_NEEDED entry and it is itself flagged DF_GNU_1_UNIQUE then
>    a proxy is generated for it.
> 
>  - Relocations via non-base namespace proxies work reliably (some code
>    paths did not do the address calculation relative to the DSO base
>    correctly when a proxy was involved).
> 
>  - Tests extended cover the above two scenarios.
> 
> Changes from v10:
> 
>  - A segfault in a dlmopen error pathway (which does not seem to have existed
>    when v10 was applied to the then-HEAD commit) has been fixed.
> 
>  - The fallback mechanism for adding DT_GNU_FLAGS_1 sections to the required
>    binaries has had some infrastructure moved to the elf/ directory
> 
>  - The runstatedir setting introduced by recent autoconf has been omitted from
>    the patchset as it is not relevant to this feature.
> 
>  - libpthread no longer tagged DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE.
>    (with both -z unique supporting linkers and if the .os hack is
>    used to add the new flag - this was inconsistent before).
> 
> Not changed:
> 
>  - There is still some diagnostic info in the config.log when the linker
>    layout is acceptable but -z unique is not yet supported. I believe this
>    _is_ useful diagnostic information as a developer might otherwise wonder
>    why the vanilla linker was being rejected when its layout output seemed
>    fine.
> 
> I have not yet implemented, but plan to address once this series is
> accepted/acceptable:
> 
>  - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)
> 
>  - Check RTLD_GLOBAL interacts properly and unsurprisingly with RTLD_SHARED.

Hi Vivek, 

This patchset looks much better than before, I have added my comments on the
set. There are some spots that need rework, like some patches that should be
merged, some style fixes, and testcase; but in general I think we can move 
forward.

I have fixed all my remarks on a personal branch [1] and checked on both
x86_64-linux-gnu and i686-linux-gnu with binutils with and without DT_GNU_FLAGS_1 
support. If you are ok with my changes, please report the patches without the RFC 
so I can ack them and I will push them upstream.

Thanks for working on this.

[1] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/azanella/rtld-shared

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO
  2021-07-08 16:32 ` [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO Vivek Das Mohapatra via Libc-alpha
  2021-08-09 20:10   ` Adhemerval Zanella via Libc-alpha
@ 2021-08-09 21:24   ` Florian Weimer via Libc-alpha
  1 sibling, 0 replies; 24+ messages in thread
From: Florian Weimer via Libc-alpha @ 2021-08-09 21:24 UTC (permalink / raw)
  To: Vivek Das Mohapatra via Libc-alpha

* Vivek Das Mohapatra via Libc-alpha:

> libpthread should have no contents that are sensitive
> to duplication anymore, so doesn't require special
> treatment under dlmopen.

htl still needs to be UNIQUE.

(Sorry for providing incorrect information earlier.)

Thanks,
Florian


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745)
  2021-08-09 16:48   ` Adhemerval Zanella via Libc-alpha
@ 2021-08-10 13:21     ` Adhemerval Zanella via Libc-alpha
  2021-08-10 13:32       ` H.J. Lu via Libc-alpha
  0 siblings, 1 reply; 24+ messages in thread
From: Adhemerval Zanella via Libc-alpha @ 2021-08-10 13:21 UTC (permalink / raw)
  To: libc-alpha, Vivek Das Mohapatra

There are a couple of minor issues that prevent the -Wl,-z,unique usage.
It does not change the build outcome because the the unique GNU_FLAGS_1
will be added by elf/dynamic-notes.os anyway.

> On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
>> diff --git a/config.make.in b/config.make.in
>> index cbf59114b0..8755490c13 100644
>> --- a/config.make.in
>> +++ b/config.make.in
>> @@ -72,6 +72,7 @@ have-cc-with-libunwind = @libc_cv_cc_with_libunwind@
>>  fno-unit-at-a-time = @fno_unit_at_a_time@
>>  bind-now = @bindnow@
>>  have-hash-style = @libc_cv_hashstyle@
>> +ld-zunique = @ld_zunique@

This is wrong, it should be '@libc_cv_ld_zunique@'
 
>> diff --git a/configure.ac b/configure.ac
>> index 34ecbba540..9369bcbebe 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -1372,9 +1372,28 @@ fi
>>  rm -f conftest*])
>>  AC_SUBST(libc_cv_hashstyle)
>>  
>> +AC_CACHE_CHECK([for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support],
> 
> Maybe a space before ' /'.
> 
>> +                libc_cv_ld_zunique, [dnl
>> +cat > conftest.ld.c <<\EOF
>> +int x (void) { return 0; }
>> +EOF
>> +
>> +libc_cv_ld_zunique=no;
>> +
>> +if AC_TRY_COMMAND([dnl
>> +${CC-cc} -Wl,--fatal-warnings -Wl,-z,unique -shared -o conftest.ld.so conftest.ld.c])
>> +then
>> +  libc_cv_ld_zunique=yes
>> +fi;
>> +ld_zunique=$libc_cv_ld_zunique
>> +rm -f conftest*])
>> +AC_SUBST(ld_zunique)

This is wrong, it should be AC_SUBST(libc_cv_ld_zunique).

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745)
  2021-08-10 13:21     ` Adhemerval Zanella via Libc-alpha
@ 2021-08-10 13:32       ` H.J. Lu via Libc-alpha
  0 siblings, 0 replies; 24+ messages in thread
From: H.J. Lu via Libc-alpha @ 2021-08-10 13:32 UTC (permalink / raw)
  To: Adhemerval Zanella; +Cc: GNU C Library

On Tue, Aug 10, 2021 at 6:21 AM Adhemerval Zanella via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
> There are a couple of minor issues that prevent the -Wl,-z,unique usage.
> It does not change the build outcome because the the unique GNU_FLAGS_1
> will be added by elf/dynamic-notes.os anyway.
>
> > On 08/07/2021 13:32, Vivek Das Mohapatra via Libc-alpha wrote:
> >> diff --git a/config.make.in b/config.make.in
> >> index cbf59114b0..8755490c13 100644
> >> --- a/config.make.in
> >> +++ b/config.make.in
> >> @@ -72,6 +72,7 @@ have-cc-with-libunwind = @libc_cv_cc_with_libunwind@
> >>  fno-unit-at-a-time = @fno_unit_at_a_time@
> >>  bind-now = @bindnow@
> >>  have-hash-style = @libc_cv_hashstyle@
> >> +ld-zunique = @ld_zunique@

No need for this.  Please use LIBC_CONFIG_VAR.

> This is wrong, it should be '@libc_cv_ld_zunique@'
>
> >> diff --git a/configure.ac b/configure.ac
> >> index 34ecbba540..9369bcbebe 100644
> >> --- a/configure.ac
> >> +++ b/configure.ac
> >> @@ -1372,9 +1372,28 @@ fi
> >>  rm -f conftest*])
> >>  AC_SUBST(libc_cv_hashstyle)
> >>
> >> +AC_CACHE_CHECK([for linker DT_GNU_FLAGS_1/ DF_GNU_1_UNIQUE support],
> >
> > Maybe a space before ' /'.
> >
> >> +                libc_cv_ld_zunique, [dnl
> >> +cat > conftest.ld.c <<\EOF
> >> +int x (void) { return 0; }
> >> +EOF
> >> +
> >> +libc_cv_ld_zunique=no;
> >> +
> >> +if AC_TRY_COMMAND([dnl
> >> +${CC-cc} -Wl,--fatal-warnings -Wl,-z,unique -shared -o conftest.ld.so conftest.ld.c])
> >> +then
> >> +  libc_cv_ld_zunique=yes
> >> +fi;
> >> +ld_zunique=$libc_cv_ld_zunique
> >> +rm -f conftest*])
> >> +AC_SUBST(ld_zunique)
>
> This is wrong, it should be AC_SUBST(libc_cv_ld_zunique).



-- 
H.J.

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2021-08-10 13:33 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-08 16:32 [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Vivek Das Mohapatra via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 1/8] Define a new dynamic section tag - DT_GNU_FLAGS_1 (bug 22745) Vivek Das Mohapatra via Libc-alpha
2021-08-09 12:27   ` Adhemerval Zanella via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 2/8] Abstract loaded-DSO search code into a helper function Vivek Das Mohapatra via Libc-alpha
2021-08-09 13:04   ` Adhemerval Zanella via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 3/8] Use the new DSO finder " Vivek Das Mohapatra via Libc-alpha
2021-08-09 12:59   ` Adhemerval Zanella via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 4/8] Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE to glibc DSOs (bug 22745) Vivek Das Mohapatra via Libc-alpha
2021-08-09 16:48   ` Adhemerval Zanella via Libc-alpha
2021-08-10 13:21     ` Adhemerval Zanella via Libc-alpha
2021-08-10 13:32       ` H.J. Lu via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag " Vivek Das Mohapatra via Libc-alpha
2021-08-09 19:07   ` Adhemerval Zanella via Libc-alpha
2021-08-09 20:24     ` Adhemerval Zanella via Libc-alpha
2021-08-09 20:30   ` Adhemerval Zanella via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 6/8] Add dlmopen / RTLD_SHARED tests Vivek Das Mohapatra via Libc-alpha
2021-08-09 20:09   ` Adhemerval Zanella via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 7/8] Restore separate libc loading for the TLS/namespace storage test Vivek Das Mohapatra via Libc-alpha
2021-08-09 19:08   ` Adhemerval Zanella via Libc-alpha
2021-07-08 16:32 ` [RFC][PATCH v12 8/8] Drop DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE from the libpthread DSO Vivek Das Mohapatra via Libc-alpha
2021-08-09 20:10   ` Adhemerval Zanella via Libc-alpha
2021-08-09 21:24   ` Florian Weimer via Libc-alpha
2021-07-13 13:08 ` [External] : [RFC][PATCH v12 0/8] Implementation of RTLD_SHARED for dlmopen Alfonso Alfonso Peterssen via Libc-alpha
2021-08-09 20:34 ` Adhemerval Zanella via Libc-alpha

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).