[RFC,v12,6/8] Add dlmopen / RTLD_SHARED tests

Message ID 20210708163255.812-7-vivek@collabora.com
State Superseded
Delegated to: Adhemerval Zanella Netto
Headers
Series Implementation of RTLD_SHARED for dlmopen |

Checks

Context Check Description
dj/TryBot-apply_patch fail Patch failed to apply to master at the time it was sent

Commit Message

Vivek Dasmohapatra July 8, 2021, 4:32 p.m. UTC
  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
  

Comments

Adhemerval Zanella Aug. 9, 2021, 8:09 p.m. UTC | #1
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.
  

Patch

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>