[RFC,v8,00/20] Implementation of RTLD_SHARED for dlmopen

Message ID 20210209171839.7911-1-vivek@collabora.com
Headers
Series Implementation of RTLD_SHARED for dlmopen |

Message

Vivek Dasmohapatra Feb. 9, 2021, 5:18 p.m. UTC
  This is a revision of a previous patchset that I posted here
regarding https://sourceware.org/bugzilla/show_bug.cgi?id=22745 

Introduction:

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

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

  This patch series should address all the comments received on the
  earlier (v1) series, and fixes a bug in the previous (v2) series
  which left the r_debug struct in an inconsistent state when creating
  a proxy triggered the initial load of a DSO into the main namespace.
=======================================================================

In addition this patch series implements the following:

 - dlmopen will implicitly apply RTLD_SHARED to the libc/libpthread group
   (requires a patched binutils/ld so that the libc family DSOs can
   be flagged as requiring this behaviour)

   - binutils patchset accepted upstream;
   - https://sourceware.org/git/?p=binutils-gdb.git
   - commit 8a87b2791181eb7fc1533ffaeb95df8d87d41493

 - LD_AUDIT paths will NOT apply this implict sharing rule:
   audit libraries will continue to be completely isolated.

 - The mechanism for tagging DSOs as implicitly shared has been changed
   from a DT_FLAGS_1 flag to a DT_VALRNGHI/LO range dynamic section tag.
   (Based on feedback on the binutils side of this patch series).

  - DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE

 - A flag RTLD_ISOLATE which is used inernally to suppress RTLD_SHARED
   behaviour when audit libraries are being loaded, and is also made available
   to users who really want a completely separate copy of glibc in their new
   namespace.

 - Tests for the new dlmopen behaviour

 - Adds the unique dso flag to htl/libpthread.so as well as nptl

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

 - Sensible RTLD_GLOBAL semantics for dlmopened DSOs in non-base namespaces

 - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)

Vivek Das Mohapatra (20):
  Declare and describe the dlmopen RTLD_SHARED flag
  include/link.h: Update the link_map struct to allow proxies
  elf/dl-object.c: Implement a helper function to proxy link_map entries
  elf/dl-load.c, elf-dl-open.c: Implement RTLD_SHARED dlmopen proxying
  elf/dl-fini.c: Handle proxy link_map entries in the shutdown path
  elf/dl-init.c: Skip proxied link map entries in the dl init path
  elf/dl-open.c: Don't try libc linit in namespaces with no libc mapping
  elf/dl-open.c: when creating a proxy check the libc_map in NS 0
  Define a new dynamic section tag - DT_GNU_FLAGS_1
  Abstract the loaded-DSO search code into a private helper function
  Compare loaded DSOs by file ID and check for DF_GNU_1_UNIQUE
  Use the new DSO finder helper function since we have it
  Use the DSO search helper to check for preloaded DT_GNU_UNIQUE DSOs
  When loading DSOs into alternate namespaces check for DT_GNU_UNIQUE
  Suppress audit calls when a (new) namespace is empty
  Suppress inter-namespace DSO sharing for audit libraries
  dlsym, dlvsym should be able to look up symbols via DSO proxies
  Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE dynamic section+flag to glibc DSOs
  Add dlmopen / RTLD_SHARED tests
  Restore separate libc loading for the TLS/namespace storage test

 Makeconfig                           |    1 +
 Makerules                            |    2 +-
 bits/dlfcn.h                         |   10 +
 elf/Makefile                         |   97 ++-
 elf/dl-close.c                       |   43 +-
 elf/dl-fini.c                        |    6 +-
 elf/dl-init.c                        |    4 +-
 elf/dl-load.c                        |  227 +++++-
 elf/dl-object.c                      |   84 +++
 elf/dl-open.c                        |  109 ++-
 elf/dl-sym.c                         |   14 +
 elf/elf.h                            |    7 +-
 elf/get-dynamic-info.h               |   12 +
 elf/rtld.c                           |    2 +-
 elf/tst-dlmopen-auditmod.c           |   23 +
 elf/tst-dlmopen-common.h             |   33 +
 elf/tst-dlmopen-main.h               | 1022 ++++++++++++++++++++++++++
 elf/tst-dlmopen-modules.h            |   20 +
 elf/tst-dlmopen-rtld-audit-shared1.c |   11 +
 elf/tst-dlmopen-rtld-audit-shared2.c |   11 +
 elf/tst-dlmopen-rtld-audit-shared3.c |   11 +
 elf/tst-dlmopen-rtld-audit-shared4.c |   11 +
 elf/tst-dlmopen-rtld-audit-shared5.c |   11 +
 elf/tst-dlmopen-rtld-audit-shared6.c |   11 +
 elf/tst-dlmopen-rtld-audit-unique1.c |   11 +
 elf/tst-dlmopen-rtld-audit-unique2.c |   11 +
 elf/tst-dlmopen-rtld-audit-unique3.c |   11 +
 elf/tst-dlmopen-rtld-audit-unique4.c |   11 +
 elf/tst-dlmopen-rtld-audit-unique5.c |   11 +
 elf/tst-dlmopen-rtld-audit-unique6.c |   11 +
 elf/tst-dlmopen-rtld-shared1.c       |   11 +
 elf/tst-dlmopen-rtld-shared1.h       |   65 ++
 elf/tst-dlmopen-rtld-shared2.c       |   11 +
 elf/tst-dlmopen-rtld-shared2.h       |   67 ++
 elf/tst-dlmopen-rtld-shared3.c       |   11 +
 elf/tst-dlmopen-rtld-shared3.h       |   44 ++
 elf/tst-dlmopen-rtld-shared4.c       |   11 +
 elf/tst-dlmopen-rtld-shared4.h       |   15 +
 elf/tst-dlmopen-rtld-shared5.c       |   11 +
 elf/tst-dlmopen-rtld-shared5.h       |   26 +
 elf/tst-dlmopen-rtld-shared6.c       |   11 +
 elf/tst-dlmopen-rtld-shared6.h       |   37 +
 elf/tst-dlmopen-rtld-unique1.c       |   11 +
 elf/tst-dlmopen-rtld-unique1.h       |   87 +++
 elf/tst-dlmopen-rtld-unique2.c       |   11 +
 elf/tst-dlmopen-rtld-unique2.h       |   26 +
 elf/tst-dlmopen-rtld-unique3.c       |   11 +
 elf/tst-dlmopen-rtld-unique3.h       |   14 +
 elf/tst-dlmopen-rtld-unique4.c       |   11 +
 elf/tst-dlmopen-rtld-unique4.h       |   15 +
 elf/tst-dlmopen-rtld-unique5.c       |   11 +
 elf/tst-dlmopen-rtld-unique5.h       |   59 ++
 elf/tst-dlmopen-rtld-unique6.c       |   11 +
 elf/tst-dlmopen-rtld-unique6.h       |   52 ++
 elf/tst-dlmopen-sharedmod-norm.c     |   11 +
 elf/tst-dlmopen-sharedmod-uniq.c     |   11 +
 elf/tst-dlmopen-std-do-test.h        |   11 +
 elf/tst-tls-ie-dlmopen.c             |    4 +-
 htl/Makefile                         |    2 +-
 iconvdata/Makefile                   |    1 +
 include/elf.h                        |    2 +
 include/link.h                       |    7 +-
 nptl/Makefile                        |    2 +-
 sysdeps/generic/ldsodefs.h           |    9 +
 sysdeps/mips/bits/dlfcn.h            |   10 +
 65 files changed, 2494 insertions(+), 63 deletions(-)
 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.h
  

Comments

Joseph Myers Feb. 9, 2021, 7:01 p.m. UTC | #1
On Tue, 9 Feb 2021, Vivek Das Mohapatra via Libc-alpha wrote:

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

Please mention the bug number in the proposed commit messages so that 
commits get properly filed in Bugzilla.  Given the size of the patch 
series I wonder if it should also add a NEWS entry for the user-visible 
change (beyond the automatically-generated one for a bug fix generated if 
the bug is RESOLVED/FIXED with the correct target milestone set at the 
time the release is made).
  
Vivek Dasmohapatra Feb. 10, 2021, 12:25 p.m. UTC | #2
> Please mention the bug number in the proposed commit messages so that
> commits get properly filed in Bugzilla.  Given the size of the patch

Any particular format? And do you want the bug # in every commit message?
  
Joseph Myers Feb. 10, 2021, 6:32 p.m. UTC | #3
On Wed, 10 Feb 2021, Vivek Das Mohapatra via Libc-alpha wrote:

> > Please mention the bug number in the proposed commit messages so that
> > commits get properly filed in Bugzilla.  Given the size of the patch
> 
> Any particular format? And do you want the bug # in every commit message?

There are various forms such as "bug 12345" or "BZ #12345" that are 
accepted by the commit processing.

Any commit that can be considered to be the one fixing the bug should 
definitely mention the number and be explicit that it is fixing that bug.  
Any commit mentioning the number without fixing the bug should be clear 
that it is relevant to the bug, but not by itself fixing the bug.
  
Adhemerval Zanella Feb. 12, 2021, 6:52 p.m. UTC | #4
On 09/02/2021 14:18, Vivek Das Mohapatra via Libc-alpha wrote:
> This is a revision of a previous patchset that I posted here
> regarding https://sourceware.org/bugzilla/show_bug.cgi?id=22745 
> 
> Introduction:
> 
> =======================================================================
>   As discussed in the URL above dlmopen requires a mechanism for
>   [optionally] sharing some objects between more than one namespace.
> 
>   The following patchset provides an implementation for this: If an
>   object is loaded with the new RTLD_SHARED flag we instead ensure
>   that a "master" copy exists (and is flagged as no-delete) in the
>   main namespace and a thin wrapper or clone is placed in the target
>   namespace.
> 
>   This patch series should address all the comments received on the
>   earlier (v1) series, and fixes a bug in the previous (v2) series
>   which left the r_debug struct in an inconsistent state when creating
>   a proxy triggered the initial load of a DSO into the main namespace.
> =======================================================================
> 
> In addition this patch series implements the following:
> 
>  - dlmopen will implicitly apply RTLD_SHARED to the libc/libpthread group
>    (requires a patched binutils/ld so that the libc family DSOs can
>    be flagged as requiring this behaviour)
> 
>    - binutils patchset accepted upstream;
>    - https://sourceware.org/git/?p=binutils-gdb.git
>    - commit 8a87b2791181eb7fc1533ffaeb95df8d87d41493
> 
>  - LD_AUDIT paths will NOT apply this implict sharing rule:
>    audit libraries will continue to be completely isolated.
> 
>  - The mechanism for tagging DSOs as implicitly shared has been changed
>    from a DT_FLAGS_1 flag to a DT_VALRNGHI/LO range dynamic section tag.
>    (Based on feedback on the binutils side of this patch series).
> 
>   - DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE
> 
>  - A flag RTLD_ISOLATE which is used inernally to suppress RTLD_SHARED
>    behaviour when audit libraries are being loaded, and is also made available
>    to users who really want a completely separate copy of glibc in their new
>    namespace.
> 
>  - Tests for the new dlmopen behaviour
> 
>  - Adds the unique dso flag to htl/libpthread.so as well as nptl

I will try to start the review of this patchset next week, I still reading
all the history and provided links but the idea is sound. And my sniff
tests did triggered any warnings.

Carlos started to review it on previous iterations, so I take he agrees
this should a good addition as well.

> 
> I have not yet implemented, but plan to address once this series is
> accepted/acceptable:
> 
>  - Sensible RTLD_GLOBAL semantics for dlmopened DSOs in non-base namespaces

Could you extend what semantic and useful would to add such extension?

> 
>  - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)

I think if we agree to add this extension we might also work around the
current issues of dl_iterate_phdr.  If I recall correctly, Florian has
raised the current scalability issues and proposed a better replacement
in a previous Cauldron.
  
Florian Weimer Feb. 12, 2021, 6:56 p.m. UTC | #5
* Adhemerval Zanella:

>>  - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)
>
> I think if we agree to add this extension we might also work around the
> current issues of dl_iterate_phdr.  If I recall correctly, Florian has
> raised the current scalability issues and proposed a better replacement
> in a previous Cauldron.

Iterating over link maps in a namespace is still indepedently useful.
I just don't think it's the right interface for locating exception
unwinding information based on a code address.

Thanks,
Florian
  
Adhemerval Zanella Feb. 12, 2021, 7:01 p.m. UTC | #6
On 12/02/2021 15:56, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>>>  - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)
>>
>> I think if we agree to add this extension we might also work around the
>> current issues of dl_iterate_phdr.  If I recall correctly, Florian has
>> raised the current scalability issues and proposed a better replacement
>> in a previous Cauldron.
> 
> Iterating over link maps in a namespace is still indepedently useful.
> I just don't think it's the right interface for locating exception
> unwinding information based on a code address.

If I recall correctly you also brought some scalability issue due the
internal locking, or am I missing something?
  
Florian Weimer Feb. 12, 2021, 7:29 p.m. UTC | #7
* Adhemerval Zanella:

> On 12/02/2021 15:56, Florian Weimer wrote:
>> * Adhemerval Zanella:
>> 
>>>>  - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)
>>>
>>> I think if we agree to add this extension we might also work around the
>>> current issues of dl_iterate_phdr.  If I recall correctly, Florian has
>>> raised the current scalability issues and proposed a better replacement
>>> in a previous Cauldron.
>> 
>> Iterating over link maps in a namespace is still indepedently useful.
>> I just don't think it's the right interface for locating exception
>> unwinding information based on a code address.
>
> If I recall correctly you also brought some scalability issue due the
> internal locking, or am I missing something?

Yes, but it's hard to tell if it matters to other use cases for
iteration without looking at them individually.

Thanks,
Florian
  
Vivek Dasmohapatra Feb. 18, 2021, 2:41 p.m. UTC | #8
>> I have not yet implemented, but plan to address once this series is
>> accepted/acceptable:
>>
>>  - Sensible RTLD_GLOBAL semantics for dlmopened DSOs in non-base namespaces
>
> Could you extend what semantic and useful would to add such extension?

Right now RTLD_GLOBAL is well defined for the main namespace.

It isn't defined at all for secondary namespaces (and tends to
result in a segfault when passed to dlmopen).

I would propose something like this:

Main namespace RTLD_GLOBAL - works as at present. The new
(or newly promoted) DSO is implicitly available for symbol
resolution from/by all main namespace DSOs.

Secondary namespace RTLD_GLOBAL - the new or newly promoted
DSO is implicitly available for symbol resolution from/by
all DSOs _in that namespace_.
  
Florian Weimer Feb. 18, 2021, 3:02 p.m. UTC | #9
* Vivek Das Mohapatra via Libc-alpha:

> I would propose something like this:
>
> Main namespace RTLD_GLOBAL - works as at present. The new
> (or newly promoted) DSO is implicitly available for symbol
> resolution from/by all main namespace DSOs.
>
> Secondary namespace RTLD_GLOBAL - the new or newly promoted
> DSO is implicitly available for symbol resolution from/by
> all DSOs _in that namespace_.

That makes sense to me.

We currently delay the update of the global scope after ELF constructors
of new objects are invoked.  Is this something we should keep (in both
cases)?

Thanks,
Florian
  
Vivek Dasmohapatra Feb. 18, 2021, 3:05 p.m. UTC | #10
> We currently delay the update of the global scope after ELF constructors
> of new objects are invoked.  Is this something we should keep (in both
> cases)?

I think so - make sure everything is consistent and settled before exposing
it to the rest of the world.
  
Florian Weimer Feb. 19, 2021, 8:57 a.m. UTC | #11
* Vivek Das Mohapatra:

>> We currently delay the update of the global scope after ELF constructors
>> of new objects are invoked.  Is this something we should keep (in both
>> cases)?
>
> I think so - make sure everything is consistent and settled before exposing
> it to the rest of the world.

But the shared objects are available on the local scope before
initialization (and it has to be this way).  So there's still an
inconsistency.

The technical problem here is that adding things to the global scope may
require memory allocation, and that can fail.  But after we have called
any ELF constructors, dlopen must not fail.  So we have to pre-allocate
any changes, and that code is a bit weird, particularly due to recursive
dlopen.

Thanks,
Florian
  
Vivek Dasmohapatra Feb. 19, 2021, 3:08 p.m. UTC | #12
> But the shared objects are available on the local scope before
> initialization (and it has to be this way).  So there's still an
> inconsistency.
>
> The technical problem here is that adding things to the global scope may
> require memory allocation, and that can fail.  But after we have called
> any ELF constructors, dlopen must not fail.  So we have to pre-allocate
> any changes, and that code is a bit weird, particularly due to recursive
> dlopen.

Hm. Well, I guess my answer would be that it should work the same way
for both cases: Otherwise we're introducing a weird (as well as obscure
and surprising) inconsistency between primary and secondary namespaces.
  
Adhemerval Zanella Feb. 26, 2021, 7:50 p.m. UTC | #13
On 09/02/2021 14:18, Vivek Das Mohapatra via Libc-alpha wrote:
> ---
>  elf/Makefile                         |   97 ++-
>  elf/tst-dlmopen-auditmod.c           |   23 +
>  elf/tst-dlmopen-common.h             |   33 +
>  elf/tst-dlmopen-main.h               | 1022 ++++++++++++++++++++++++++
>  elf/tst-dlmopen-modules.h            |   20 +
>  elf/tst-dlmopen-rtld-audit-shared1.c |   11 +
>  elf/tst-dlmopen-rtld-audit-shared2.c |   11 +
>  elf/tst-dlmopen-rtld-audit-shared3.c |   11 +
>  elf/tst-dlmopen-rtld-audit-shared4.c |   11 +
>  elf/tst-dlmopen-rtld-audit-shared5.c |   11 +
>  elf/tst-dlmopen-rtld-audit-shared6.c |   11 +
>  elf/tst-dlmopen-rtld-audit-unique1.c |   11 +
>  elf/tst-dlmopen-rtld-audit-unique2.c |   11 +
>  elf/tst-dlmopen-rtld-audit-unique3.c |   11 +
>  elf/tst-dlmopen-rtld-audit-unique4.c |   11 +
>  elf/tst-dlmopen-rtld-audit-unique5.c |   11 +
>  elf/tst-dlmopen-rtld-audit-unique6.c |   11 +
>  elf/tst-dlmopen-rtld-shared1.c       |   11 +
>  elf/tst-dlmopen-rtld-shared1.h       |   65 ++
>  elf/tst-dlmopen-rtld-shared2.c       |   11 +
>  elf/tst-dlmopen-rtld-shared2.h       |   67 ++
>  elf/tst-dlmopen-rtld-shared3.c       |   11 +
>  elf/tst-dlmopen-rtld-shared3.h       |   44 ++
>  elf/tst-dlmopen-rtld-shared4.c       |   11 +
>  elf/tst-dlmopen-rtld-shared4.h       |   15 +
>  elf/tst-dlmopen-rtld-shared5.c       |   11 +
>  elf/tst-dlmopen-rtld-shared5.h       |   26 +
>  elf/tst-dlmopen-rtld-shared6.c       |   11 +
>  elf/tst-dlmopen-rtld-shared6.h       |   37 +
>  elf/tst-dlmopen-rtld-unique1.c       |   11 +
>  elf/tst-dlmopen-rtld-unique1.h       |   87 +++
>  elf/tst-dlmopen-rtld-unique2.c       |   11 +
>  elf/tst-dlmopen-rtld-unique2.h       |   26 +
>  elf/tst-dlmopen-rtld-unique3.c       |   11 +
>  elf/tst-dlmopen-rtld-unique3.h       |   14 +
>  elf/tst-dlmopen-rtld-unique4.c       |   11 +
>  elf/tst-dlmopen-rtld-unique4.h       |   15 +
>  elf/tst-dlmopen-rtld-unique5.c       |   11 +
>  elf/tst-dlmopen-rtld-unique5.h       |   59 ++
>  elf/tst-dlmopen-rtld-unique6.c       |   11 +
>  elf/tst-dlmopen-rtld-unique6.h       |   52 ++
>  elf/tst-dlmopen-sharedmod-norm.c     |   11 +
>  elf/tst-dlmopen-sharedmod-uniq.c     |   11 +
>  elf/tst-dlmopen-std-do-test.h        |   11 +
>  44 files changed, 1998 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.h
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 16c89b6d07..bccf31b1a9 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
> +

Ok, default RTLD_SHARED tests.

> +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

Ok.  The main difference between the rtld-shared and rtld-unique tests
is the usage of DSO_INIQUE instead of DSO_NORMAL which is used on second
argument for dlmopen.

> +
> +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

Ok.  The main difference between the rtld-shared and rtld-audir tests
is the new expected ns is different (EXPECTED_NS).


> +
> +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
> +

Ok.  The main difference between the rtld-unique and rtld-audit-unique
tests is the usage of DSO_INIQUE instead of DSO_NORMAL which is used on
seconda argument for dlmopen.


>  tests += restest1 preloadtest loadfail multiload origtest resolvfail \
>  	 constload1 order noload filter \
>  	 reldep reldep2 reldep3 reldep4 nodelete nodelete2 \
> @@ -225,7 +257,11 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
>  	 tst-audit14 tst-audit15 tst-audit16 \
>  	 tst-single_threaded tst-single_threaded-pthread \
>  	 tst-tls-ie tst-tls-ie-dlmopen argv0test \
> -	 tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask
> +	 tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask \
> +	 $(dlmopen-rtld-tests-norm) \
> +	 $(dlmopen-rtld-tests-uniq) \
> +	 $(dlmopen-rtld-audit-tests-norm) \
> +	 $(dlmopen-rtld-audit-tests-uniq)
>  #	 reldep9
>  tests-internal += loadtest unload unload2 circleload1 \
>  	 neededtest neededtest2 neededtest3 neededtest4 \

Ok.

> @@ -286,6 +322,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 \
> @@ -325,6 +364,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 \

Ok.

> @@ -823,6 +863,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.
> @@ -1301,6 +1344,58 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
>  $(objpfx)tst-dlmopen3: $(libdl)
>  $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
>  
> +LDFLAGS-tst-dlmopen-sharedmod-uniq.so = -Wl,-z,unique

It incur in the same binutils support noted in the
'DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE dynamic section+flag' patch.  So if the idea
to to with the trick to add the required dynamic section with the trick of
using the linker script, we will need to use it here as well.

The only issue is it probably also require to add a new rule to create a
specific linker script to shared object tests (to add the .dynamic {}
trick).

> +$(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
>  

Ok.

> diff --git a/elf/tst-dlmopen-auditmod.c b/elf/tst-dlmopen-auditmod.c
> new file mode 100644
> index 0000000000..04457249d0
> --- /dev/null
> +++ b/elf/tst-dlmopen-auditmod.c
> @@ -0,0 +1,23 @@
> +/* Audit module for tst-dlmopen-rtld-audit-*
> +   Copyright © 2020 Free Software Foundation, Inc.

s/2020/2021

> +   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..5653c9c02c
> --- /dev/null
> +++ b/elf/tst-dlmopen-common.h
> @@ -0,0 +1,33 @@
> +#pragma once
> +

There is no other usage of pragma once and I don't see an impeding reason
to not use.  However I would prefer to go in a conservative way and use
include guards, I will send a email about the pragma once on libc-alpha
and see if there any impending reason to start adopt in more places.

Also, move the *after* the copyright header.

> +/* Common infrastructure for tst-dlmopen-rtld-*
> +   Copyright © 2020 Free Software Foundation, Inc.

s/2020/s2021

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

Extra new line.

> diff --git a/elf/tst-dlmopen-main.h b/elf/tst-dlmopen-main.h
> new file mode 100644
> index 0000000000..2e091e9b8d
> --- /dev/null
> +++ b/elf/tst-dlmopen-main.h
> @@ -0,0 +1,1022 @@
> +#pragma once
> +

Same as before regarding 'pragma once'.

> +/* Main infrastructure for tst-dlmopen-rtld-*
> +   Copyright © 2020 Free Software Foundation, Inc.

s/2020/s2021

> +   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/>.  */
> +
> +/* dlmopen ± RTLD_SHARED/RTLD_ISOLATE semantics:

I would prefer to just use ascii, even on comments (s/±/+-).

> +
> +   RTLD_ISOLATE's purpose is to suppress all shared behaviour,
> +   mainly used fir LD_AUDIT code paths but available to the user

s/fir/for

> +   and also useful for constructing test case preconditions.
> +
> +   dlmopen should have the following behaviour:
> +
> +   Notation:
> +        Number of namespace (+ = make a new one in an empty NS)

Maybe use "'+' means make a new one in an empty NS" .

> +        |
> +     [+]X[p] - p indicates a proxy
> +      |
> +      + → new enry after the dlmopen call

s/→/->

> +
> +      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)
> +
> +      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

Maybe add that each line represents a different test here?


> +      =======+===========+============+========+===========+=========
> +      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>
> +
> +#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 END_TESTS { .name = NULL },
> +
> +#define ERROR(test, fmt, ...) \
> +  ({ if (last_test != test)                                        \
> +       printf ("FAILED: %s (%s):\n", test->name, test->desc);      \
> +     printf ("%s @ %d - " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
> +     last_test = test; })

Use support/check.h here.

> +
> +static void *last_test;
> +
> +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;
> +
> +struct r_scope_elem
> +{
> +  struct tst_lm **r_list;
> +  unsigned int r_nlist;
> +};
> +
> +/* This is a copy of the first part of the internal definition of
> +   struct link_map from <include/link.h>.  We use it to check some
> +   expected internal state that's not readily accessible via public APIs.
> + */

I think these tests should be marked as 'tests-internal' so you can include
the internal definition instead of replicate the defition below.

> +struct tst_lm
> +  {
> +    /* These first few members are part of the protocol with the debugger.
> +       This is the same format used in SVR4.  */
> +
> +    ElfW(Addr) l_addr;		/* Difference between the address in the ELF
> +                                   file and the addresses in memory.  */
> +    char *l_name;		/* Absolute file name object was found in.  */
> +    ElfW(Dyn) *l_ld;		/* Dynamic section of the shared object.  */
> +    struct tst_lm *l_next, *l_prev; /* Chain of loaded objects.  */
> +
> +    /* All following members are internal to the dynamic linker.
> +       They may change without notice.  */
> +
> +    /* This is an element which is only ever different from a pointer to
> +       the very same copy of this type when:
> +       - A shallow copy of ld.so is placed in namespaces other than LM_ID_BASE.
> +       - An object is proxied into a namespace by dlmopen with RTLD_SHARED.  */
> +    struct tst_lm *l_real;
> +
> +    /* Number of the namespace this link map belongs to.  */
> +    Lmid_t l_ns;
> +
> +    struct libname_list *l_libname;
> +    /* Indexed pointers to dynamic section.
> +       [0,DT_NUM) are indexed by the processor-independent tags.
> +       [DT_NUM,DT_NUM+DT_THISPROCNUM) are indexed by the tag minus DT_LOPROC.
> +       [DT_NUM+DT_THISPROCNUM,DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM) are
> +       indexed by DT_VERSIONTAGIDX(tagvalue).
> +       [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM,
> +        DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) are indexed by
> +       DT_EXTRATAGIDX(tagvalue).
> +       [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM,
> +        DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM) are
> +       indexed by DT_VALTAGIDX(tagvalue) and
> +       [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM,
> +        DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM+DT_ADDRNUM)
> +       are indexed by DT_ADDRTAGIDX(tagvalue), see <elf.h>.  */
> +
> +    ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM
> +                      + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];
> +    const ElfW(Phdr) *l_phdr;	/* Pointer to program header table in core.  */
> +    ElfW(Addr) l_entry;		/* Entry point location.  */
> +    ElfW(Half) l_phnum;		/* Number of program header entries.  */
> +    ElfW(Half) l_ldnum;		/* Number of dynamic segment entries.  */
> +
> +    /* Array of DT_NEEDED dependencies and their dependencies, in
> +       dependency order for symbol lookup (with and without
> +       duplicates).  There is no entry before the dependencies have
> +       been loaded.  */
> +    struct r_scope_elem l_searchlist;
> +
> +    /* We need a special searchlist to process objects marked with
> +       DT_SYMBOLIC.  */
> +    struct r_scope_elem l_symbolic_searchlist;
> +
> +    /* Dependent object that first caused this object to be loaded.  */
> +    struct link_map *l_loader;
> +
> +    /* Array with version names.  */
> +    struct r_found_version *l_versions;
> +    unsigned int l_nversions;
> +
> +    /* Symbol hash table.  */
> +    Elf_Symndx l_nbuckets;
> +    Elf32_Word l_gnu_bitmask_idxbits;
> +    Elf32_Word l_gnu_shift;
> +    const ElfW(Addr) *l_gnu_bitmask;
> +    union
> +    {
> +      const Elf32_Word *l_gnu_buckets;
> +      const Elf_Symndx *l_chain;
> +    };
> +    union
> +    {
> +      const Elf32_Word *l_gnu_chain_zero;
> +      const Elf_Symndx *l_buckets;
> +    };
> +
> +    unsigned int l_direct_opencount; /* Reference count for dlopen/dlclose.  */
> +    enum			/* Where this object came from.  */
> +      {
> +        lt_executable,		/* The main executable program.  */
> +        lt_library,		/* Library needed by main executable.  */
> +        lt_loaded		/* Extra run-time loaded shared object.  */
> +      } l_type:2;
> +    unsigned int l_relocated:1;	/* Nonzero if object's relocations done.  */
> +    unsigned int l_init_called:1; /* Nonzero if DT_INIT function called.  */
> +    unsigned int l_global:1;	/* Nonzero if object in _dl_global_scope.  */
> +    unsigned int l_proxy:1;    /* Nonzero if object is a shallow copy.  */
> +    unsigned int l_reserved:2;	/* Reserved for internal use.  */
> +    unsigned int l_phdr_allocated:1; /* Nonzero if the data structure pointed
> +                                        to by `l_phdr' is allocated.  */
> +    unsigned int l_soname_added:1; /* Nonzero if the SONAME is for sure in
> +                                      the l_libname list.  */
> +    unsigned int l_faked:1;	/* Nonzero if this is a faked descriptor
> +                                   without associated file.  */
> +    unsigned int l_need_tls_init:1; /* Nonzero if GL(dl_init_static_tls)
> +                                       should be called on this link map
> +                                       when relocation finishes.  */
> +    unsigned int l_auditing:1;	/* Nonzero if the DSO is used in auditing.  */
> +    unsigned int l_audit_any_plt:1; /* Nonzero if at least one audit module
> +                                       is interested in the PLT interception.*/
> +    unsigned int l_removed:1;	/* Nozero if the object cannot be used anymore
> +                                   since it is removed.  */
> +    unsigned int l_contiguous:1; /* Nonzero if inter-segment holes are
> +                                    mprotected or if no holes are present at
> +                                    all.  */
> +    unsigned int l_symbolic_in_local_scope:1; /* Nonzero if l_local_scope
> +                                                 during LD_TRACE_PRELINKING=1
> +                                                 contains any DT_SYMBOLIC
> +                                                 libraries.  */
> +    unsigned int l_free_initfini:1; /* Nonzero if l_initfini can be
> +                                       freed, ie. not allocated with
> +                                       the dummy malloc in ld.so.  */
> +  };
> +
> +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 < sizeof(cached_handles)/sizeof(test_handle); i++)

Use array_length.h here.

> +    if (cached_handles[i].handle == NULL)
> +      {
> +        cached_handles[i].handle = handle;
> +        cached_handles[i].ns = ns;
> +        break;
> +      }
> +}
> +
> +__attribute__((unused))
> +static struct tst_lm *
> +link_map_of_dl_handle (void *handle)
> +{
> +  struct tst_lm *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;
> +}
> +

Ok.

> +__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;
> +}
> +

Ok.

> +__attribute__((unused))
> +static Lmid_t real_ns_of_dl_handle (void *handle)
> +{
> +  Lmid_t ns = 0;
> +  struct tst_lm *lm = link_map_of_dl_handle (handle);
> +
> +  if (lm == NULL)
> +    return -1;
> +
> +  // printf ("DEBUG: handle %p; LM %p; proxy: %d / %p\n",
> +  // handle, lm, lm->l_proxy, lm->l_real);

Comments should be in C style (/* */).  Same for other occurances.

> +
> +  if (lm->l_proxy)
> +    {
> +      // printf ("DEBUG: proxy %p vs real %p\n", lm, lm->l_real);
> +      ns = ns_of_dl_handle ((void *) lm->l_real);
> +    }

Usually in such cases there is no need to add brackets.

> +  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;
> +}
> +

Ok.

> +__attribute__((unused))
> +static const char *lm_name (struct tst_lm *lm)
> +{
> +  if (lm)
> +    return lm->l_name;
> +
> +  return NULL;
> +}
> +

Ok.

> +
> +static int dlm_dso_is_loaded (void *handle)
> +{
> +  if (handle != RTLD_DEFAULT)
> +    {
> +#ifdef DEBUG_DSO_LOADCHECK
> +      Dl_info sinfo = {};
> +#endif

Is is a left over of development debugging? Otherwise you can add this
as verbose output (check support/test-driver.h and test_verbose internal
usage).

However I think this are useful and it would be better to add as
default for the tests.

> +
> +      if (((struct tst_lm *) handle)->l_type != lt_loaded)
> +        return 0;
> +
> +#ifdef DEBUG_DSO_LOADCHECK
> +      printf ("checking %p %s for %s\n",
> +              handle, ((struct tst_lm *)handle)->l_name, DSO_TESTFN);
> +#endif
> +
> +      void *symbol = dlsym (handle, DSO_TESTFN);
> +
> +#ifdef DEBUG_DSO_LOADCHECK
> +      dladdr (symbol, &sinfo);
> +      printf ("  -> %s (in %s (%p))\n",
> +              sinfo.dli_fname,
> +              sinfo.dli_sname,
> +              sinfo.dli_saddr);
> +#endif
> +
> +      return symbol ? 1 : 0;
> +    }
> +
> +  for (int i = 0; i < sizeof(cached_handles)/sizeof(test_handle); i++)

Use array_length.h here.

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

Ok.

> +
> +static int call_testfunc (dlmopen_test_spec *test, void *handle)

I think the code guidelines state function name should be after a new line.

> +{
> +  Dl_info dli = {};
> +  struct tst_lm *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);

Use support/check.h functions here and in other failures cases.

> +      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;
> +}

Ok.

> +
> +static void *link_map_snapshot_array (void *handle, void *func, Lmid_t ns, size_t *len)
> +{
> +  struct tst_lm *lm = NULL;
> +  struct tst_lm *start = NULL;
> +
> +  if (len)

No implict checks.

> +    *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 < sizeof(cached_handles)/sizeof(test_handle); i++)

Use array_length.h here.

> +        {
> +          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)

No implicit checks.

> +    start = start->l_prev;
> +
> +  size_t lm_size = 0;
> +
> +  for (lm = start; lm; lm = lm->l_next)

No implicits checks.

> +    lm_size++;
> +
> +  struct tst_lm **lm_list = calloc (lm_size + 1, sizeof (struct tst_lm *));

Use xcalloc here.

> +
> +  if (len)
> +    *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 tst_lm **lma, size_t len, void *handle)
> +{
> +  if (lma == NULL)
> +    return 0;
> +
> +  if (len == 0)
> +    return 0;
> +  
> +  struct tst_lm *target = link_map_of_dl_handle (handle);
> +
> +  for (int i = 0; i < len; i++)
> +    {
> +      //printf ("searching for %p in <%p>[%d] == %p\n",
> +      //        handle, lma, i, lma[i]);
> +      if (handle == (struct tst_lm *)(lma[i]))
> +        return 1;
> +
> +      if (target->l_proxy)
> +        if (target->l_real == (struct tst_lm *)(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 tst_lm *
> +find_test_dso_in_link_map_array (struct tst_lm **lma, size_t len)
> +{
> +  if (lma == NULL)
> +    return NULL;
> +
> +  if (len == 0)
> +    return NULL;
> +
> +  for (int i = 0; i < len; i++)
> +    {
> +      if (!lma[i] || !lma[i]->l_name)

No implicit checks with pointers.

> +        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 tst_lm *)lma[i];
> +    }
> +  
> +  return NULL;
> +}
> +
> +__attribute__((unused))
> +static void *link_map_list (void *handle, void *func, int *len, Lmid_t *ns)
> +{
> +  struct tst_lm *lm = NULL;
> +  struct tst_lm *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)
> +    start = start->l_prev;
> +
> +  size_t lm_size = 0;
> +
> +  for (lm = start; lm; lm = lm->l_next)
> +    lm_size++;
> +
> +  if (len)
> +    *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 int process_test_spec (dlmopen_test_spec *test)

Maybe return bool here.

> +{
> +  void *handle = NULL;
> +  size_t lm_before_len[MAX_NS] = { 0 };
> +  size_t lm_after_len[MAX_NS] = { 0 };
> +  struct tst_lm **lm_before[MAX_NS] = { NULL };
> +  struct tst_lm **lm_after[MAX_NS] = { NULL };
> +  struct tst_lm *preloads[MAX_NS] = { NULL };
> +  int want_preload = 0;
> +  int test_status = 0;
> +
> +  memset (&lm_after[0], 0, sizeof (lm_after));
> +  memset (&lm_before[0], 0, sizeof (lm_before));
> +  memset (&preloads[0], 0, sizeof (preloads));

You already initialized all arrays.

> +
> +  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 tst_lm **lm = lm_before[i];
> +          want_preload++;
> +
> +          if (lm != NULL)
> +            for (int j = 0; !preloads[i] && (j < lm_before_len[i]); j++)
> +              if (dlm_dso_is_loaded (lm[j]) && lm[j]->l_proxy)
> +                preloads[i] = lm[j];
> +
> +          if (!preloads[i])

No implicit checks.

> +            {
> +              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 tst_lm **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)");

Ok.

> +  
> +  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);

Ok.

> +
> +  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";
> +
> +      if (test->failure)
> +        {
> +          status = "failed (EXPECTED)";
> +          test_status = 1;
> +
> +          printf ("%s: dlmopen(%s, %d, 0x%0x) failed: OK\n",
> +                  test->name, test->args.dso_path,
> +                  (int)test->args.ns, (int)test->args.flags);
> +          printf ("Returned: %p\n\n", handle);
> +
> +          goto cleanup;
> +        }
> +
> +      ERROR (test, "");
> +
> +      if (test->is_prep_stage)
> +        printf ("(during setup of preconditions): ");
> +      else
> +        printf (": ");
> +
> +      if (test->args.ns == LM_ID_BASE)
> +        printf ("dlmopen (LM_ID_BASE, \"%s\", 0x%x) %s: %s\n",
> +                test->args.dso_path, test->args.flags, status, dlerror ());
> +      else
> +        printf ("dlmopen (%d, \"%s\", 0x%x) %s: %s\n",
> +                (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");

Ok.

> +
> +  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);
> +

Ok.

> +  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);

Ok.

> +
> +  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");

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 in %s\n", i, __FILE__);
> +              goto cleanup;
> +            }
> +        }
> +
> +      if (test->loaded[i] & PROXY)
> +        {
> +          TRACE ("rechecking DSO status in ns %d", i);
> +          if (!((struct tst_lm *)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 tst_lm *)new_handle)->l_proxy)
> +            {
> +              ERROR (test, "DSO in ns %d should NOT be a proxy but is\n", i);
> +              goto cleanup;
> +            }
> +        }
> +    }
> +
> +  test_status = 1;

Ok.

> +
> + 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;
> +}
> +
> +

Ok.

> diff --git a/elf/tst-dlmopen-modules.h b/elf/tst-dlmopen-modules.h
> new file mode 100644
> index 0000000000..4bbf431ec3
> --- /dev/null
> +++ b/elf/tst-dlmopen-modules.h
> @@ -0,0 +1,20 @@
> +#pragma once
> +/* Module-specific infrastructure for tst-dlmopen-rtld-*
> +   Copyright © 2020 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.

s/2020/s2021

> +
> +   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/>.  */
> +
> +#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..1e9f604327
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared1.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared1.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared2.c b/elf/tst-dlmopen-rtld-audit-shared2.c
> new file mode 100644
> index 0000000000..53064dd345
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared2.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared2.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared3.c b/elf/tst-dlmopen-rtld-audit-shared3.c
> new file mode 100644
> index 0000000000..de90bc1258
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared3.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared3.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.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..c600b2f4dc
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared4.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared4.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared5.c b/elf/tst-dlmopen-rtld-audit-shared5.c
> new file mode 100644
> index 0000000000..cd223d041b
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared5.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared5.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-shared6.c b/elf/tst-dlmopen-rtld-audit-shared6.c
> new file mode 100644
> index 0000000000..f84303b1b4
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-shared6.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-shared6.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-audit-unique1.c b/elf/tst-dlmopen-rtld-audit-unique1.c
> new file mode 100644
> index 0000000000..f60b9c05d4
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique1.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique1.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.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..199b1d3606
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique2.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique2.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.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..f3ebe58828
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique3.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique3.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.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..ef555672d2
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique4.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique4.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.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..b7faa5d02c
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique5.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique5.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.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..d431e3a787
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-audit-unique6.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 2
> +#include "tst-dlmopen-rtld-unique6.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared1.c b/elf/tst-dlmopen-rtld-shared1.c
> new file mode 100644
> index 0000000000..812d21e692
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared1.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared1.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared1.h b/elf/tst-dlmopen-rtld-shared1.h
> new file mode 100644
> index 0000000000..bd733cb3c4
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared1.h
> @@ -0,0 +1,65 @@
> +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,
> +   },
> +   END_TESTS
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared2.c b/elf/tst-dlmopen-rtld-shared2.c
> new file mode 100644
> index 0000000000..f830832a0a
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared2.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared2.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared2.h b/elf/tst-dlmopen-rtld-shared2.h
> new file mode 100644
> index 0000000000..9775619b9a
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared2.h
> @@ -0,0 +1,67 @@
> +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 },
> +   },
> +   END_TESTS
> +  };

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared3.c b/elf/tst-dlmopen-rtld-shared3.c
> new file mode 100644
> index 0000000000..a63753eb84
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared3.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared3.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>

Ok.

> diff --git a/elf/tst-dlmopen-rtld-shared3.h b/elf/tst-dlmopen-rtld-shared3.h
> new file mode 100644
> index 0000000000..23fce58bae
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared3.h
> @@ -0,0 +1,44 @@
> +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 },
> +   },
> +   END_TESTS
> +  };


Ok.


> diff --git a/elf/tst-dlmopen-rtld-shared4.c b/elf/tst-dlmopen-rtld-shared4.c
> new file mode 100644
> index 0000000000..7c3d92e37b
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared4.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared4.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-shared4.h b/elf/tst-dlmopen-rtld-shared4.h
> new file mode 100644
> index 0000000000..9ad29b9a6d
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared4.h
> @@ -0,0 +1,15 @@
> +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 },
> +   },
> +   END_TESTS
> +  };


Ok.


> diff --git a/elf/tst-dlmopen-rtld-shared5.c b/elf/tst-dlmopen-rtld-shared5.c
> new file mode 100644
> index 0000000000..f59d14e7b6
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared5.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared5.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-shared5.h b/elf/tst-dlmopen-rtld-shared5.h
> new file mode 100644
> index 0000000000..220129a5cf
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared5.h
> @@ -0,0 +1,26 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-rtld-shared6.c b/elf/tst-dlmopen-rtld-shared6.c
> new file mode 100644
> index 0000000000..469fb3566d
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared6.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-shared6.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-shared6.h b/elf/tst-dlmopen-rtld-shared6.h
> new file mode 100644
> index 0000000000..116e53ea51
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-shared6.h
> @@ -0,0 +1,37 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-rtld-unique1.c b/elf/tst-dlmopen-rtld-unique1.c
> new file mode 100644
> index 0000000000..867345582f
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique1.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique1.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-unique1.h b/elf/tst-dlmopen-rtld-unique1.h
> new file mode 100644
> index 0000000000..f7d32959c2
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique1.h
> @@ -0,0 +1,87 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-rtld-unique2.c b/elf/tst-dlmopen-rtld-unique2.c
> new file mode 100644
> index 0000000000..dabb87b43f
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique2.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique2.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-unique2.h b/elf/tst-dlmopen-rtld-unique2.h
> new file mode 100644
> index 0000000000..911e0180ab
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique2.h
> @@ -0,0 +1,26 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-rtld-unique3.c b/elf/tst-dlmopen-rtld-unique3.c
> new file mode 100644
> index 0000000000..db8a567a3f
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique3.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique3.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-unique3.h b/elf/tst-dlmopen-rtld-unique3.h
> new file mode 100644
> index 0000000000..b400bad07f
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique3.h
> @@ -0,0 +1,14 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-rtld-unique4.c b/elf/tst-dlmopen-rtld-unique4.c
> new file mode 100644
> index 0000000000..58b8e017b4
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique4.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique4.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-unique4.h b/elf/tst-dlmopen-rtld-unique4.h
> new file mode 100644
> index 0000000000..36d5915172
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique4.h
> @@ -0,0 +1,15 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-rtld-unique5.c b/elf/tst-dlmopen-rtld-unique5.c
> new file mode 100644
> index 0000000000..b2e8329461
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique5.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique5.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-unique5.h b/elf/tst-dlmopen-rtld-unique5.h
> new file mode 100644
> index 0000000000..29882ea782
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique5.h
> @@ -0,0 +1,59 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-rtld-unique6.c b/elf/tst-dlmopen-rtld-unique6.c
> new file mode 100644
> index 0000000000..fecaf559a3
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique6.c
> @@ -0,0 +1,11 @@
> +#include <dlfcn.h>
> +#include "tst-dlmopen-main.h"
> +
> +#define EXPECTED_NS 1
> +#include "tst-dlmopen-rtld-unique6.h"
> +
> +#include "tst-dlmopen-std-do-test.h"
> +
> +DEFINE_DLMOPEN_TEST(dltest)
> +
> +#include <support/test-driver.c>
> diff --git a/elf/tst-dlmopen-rtld-unique6.h b/elf/tst-dlmopen-rtld-unique6.h
> new file mode 100644
> index 0000000000..3c25127fa1
> --- /dev/null
> +++ b/elf/tst-dlmopen-rtld-unique6.h
> @@ -0,0 +1,52 @@
> +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 },
> +   },
> +   END_TESTS
> +  };
> diff --git a/elf/tst-dlmopen-sharedmod-norm.c b/elf/tst-dlmopen-sharedmod-norm.c
> new file mode 100644
> index 0000000000..fd049903f4
> --- /dev/null
> +++ b/elf/tst-dlmopen-sharedmod-norm.c
> @@ -0,0 +1,11 @@
> +#include "tst-dlmopen-modules.h"
> +
> +dlmopen_testresult *rtld_shared_testfunc (void)
> +{
> +  static dlmopen_testresult result;
> +
> +  result.name = "norm";
> +  result.free = free;
> +
> +  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..5c9701da41
> --- /dev/null
> +++ b/elf/tst-dlmopen-sharedmod-uniq.c
> @@ -0,0 +1,11 @@
> +#include "tst-dlmopen-modules.h"
> +
> +dlmopen_testresult *rtld_shared_testfunc (void)
> +{
> +  static dlmopen_testresult result;
> +
> +  result.name = "noop";
> +  result.free = free;
> +
> +  return &result;
> +}


Ok.


> diff --git a/elf/tst-dlmopen-std-do-test.h b/elf/tst-dlmopen-std-do-test.h
> new file mode 100644
> index 0000000000..595f6f764d
> --- /dev/null
> +++ b/elf/tst-dlmopen-std-do-test.h
> @@ -0,0 +1,11 @@
> +#pragma once
> +
> +#define DEFINE_DLMOPEN_TEST(x) \
> +  static int                          \
> +  do_test (void)                      \
> +  {                                   \
> +    for (int i = 0; x[i].name; i++)   \
> +      if (!process_test_spec (&x[i])) \
> +        return 1;                     \
> +    return 0;                         \
> +  }
> 

You can the array_length macro to avoid add a END_TESTS and maybe define
it a .c file instead of macro:

---
$ cat elf/tst-dlmopen-std-do-test.c
#include <array-lenght.h>

static int
do_test (void)
{
  for (int i = 0; i < array_length (tests); i++)
    if (!process_test_spec (&x[i]))
      return 1;
  return 0;
}
#include <support/test-driver.c>
---

So you can just add a '#include "tst-dlmopen-std-do-test.c"' instead.
  
Andreas Schwab Feb. 26, 2021, 8:26 p.m. UTC | #14
On Feb 09 2021, Vivek Das Mohapatra via Libc-alpha wrote:

> diff --git a/elf/tst-dlmopen-auditmod.c b/elf/tst-dlmopen-auditmod.c
> new file mode 100644
> index 0000000000..04457249d0
> --- /dev/null
> +++ b/elf/tst-dlmopen-auditmod.c
> @@ -0,0 +1,23 @@
> +/* Audit module for tst-dlmopen-rtld-audit-*
> +   Copyright © 2020 Free Software Foundation, Inc.

Please don't use non-ascii in sources.

Andreas.
  
Adhemerval Zanella March 4, 2021, 6:27 p.m. UTC | #15
On 09/02/2021 14:18, Vivek Das Mohapatra via Libc-alpha wrote:
> This is a revision of a previous patchset that I posted here
> regarding https://sourceware.org/bugzilla/show_bug.cgi?id=22745 
> 
> Introduction:
> 
> =======================================================================
>   As discussed in the URL above dlmopen requires a mechanism for
>   [optionally] sharing some objects between more than one namespace.
> 
>   The following patchset provides an implementation for this: If an
>   object is loaded with the new RTLD_SHARED flag we instead ensure
>   that a "master" copy exists (and is flagged as no-delete) in the
>   main namespace and a thin wrapper or clone is placed in the target
>   namespace.
> 
>   This patch series should address all the comments received on the
>   earlier (v1) series, and fixes a bug in the previous (v2) series
>   which left the r_debug struct in an inconsistent state when creating
>   a proxy triggered the initial load of a DSO into the main namespace.
> =======================================================================
> 
> In addition this patch series implements the following:
> 
>  - dlmopen will implicitly apply RTLD_SHARED to the libc/libpthread group
>    (requires a patched binutils/ld so that the libc family DSOs can
>    be flagged as requiring this behaviour)
> 
>    - binutils patchset accepted upstream;
>    - https://sourceware.org/git/?p=binutils-gdb.git
>    - commit 8a87b2791181eb7fc1533ffaeb95df8d87d41493
> 
>  - LD_AUDIT paths will NOT apply this implict sharing rule:
>    audit libraries will continue to be completely isolated.
> 
>  - The mechanism for tagging DSOs as implicitly shared has been changed
>    from a DT_FLAGS_1 flag to a DT_VALRNGHI/LO range dynamic section tag.
>    (Based on feedback on the binutils side of this patch series).
> 
>   - DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE
> 
>  - A flag RTLD_ISOLATE which is used inernally to suppress RTLD_SHARED
>    behaviour when audit libraries are being loaded, and is also made available
>    to users who really want a completely separate copy of glibc in their new
>    namespace.
> 
>  - Tests for the new dlmopen behaviour
> 
>  - Adds the unique dso flag to htl/libpthread.so as well as nptl
> 
> I have not yet implemented, but plan to address once this series is
> accepted/acceptable:
> 
>  - Sensible RTLD_GLOBAL semantics for dlmopened DSOs in non-base namespaces
> 
>  - dl_iterate_ns_phdr (cf dl_iterate_phdr but taking a namespace argument)
> 
> Vivek Das Mohapatra (20):
>   Declare and describe the dlmopen RTLD_SHARED flag
>   include/link.h: Update the link_map struct to allow proxies
>   elf/dl-object.c: Implement a helper function to proxy link_map entries
>   elf/dl-load.c, elf-dl-open.c: Implement RTLD_SHARED dlmopen proxying
>   elf/dl-fini.c: Handle proxy link_map entries in the shutdown path
>   elf/dl-init.c: Skip proxied link map entries in the dl init path
>   elf/dl-open.c: Don't try libc linit in namespaces with no libc mapping
>   elf/dl-open.c: when creating a proxy check the libc_map in NS 0
>   Define a new dynamic section tag - DT_GNU_FLAGS_1
>   Abstract the loaded-DSO search code into a private helper function
>   Compare loaded DSOs by file ID and check for DF_GNU_1_UNIQUE
>   Use the new DSO finder helper function since we have it
>   Use the DSO search helper to check for preloaded DT_GNU_UNIQUE DSOs
>   When loading DSOs into alternate namespaces check for DT_GNU_UNIQUE
>   Suppress audit calls when a (new) namespace is empty
>   Suppress inter-namespace DSO sharing for audit libraries
>   dlsym, dlvsym should be able to look up symbols via DSO proxies
>   Add DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE dynamic section+flag to glibc DSOs
>   Add dlmopen / RTLD_SHARED tests
>   Restore separate libc loading for the TLS/namespace storage test

Now that I have reviewed all the patches, I think the set should be reorganized
to each patch being logically consistent and not requiring to have all patches
applied in a bulk to get RTLD_SHARED/RTLD_ISOLATE support fully implemented.

So besides fixing all the implicit and style issues (missing space, attribute 
out of 'if', etc.) I think the patch should be logically implemented as:

  1. Move the 09/20 to first in set (it adds the new binutils definitions
     and set the l_gnu_flags_1).  The new definitions are used only 
     internally and the new flag is only set but not used on the patch.

  2. Move the 10/20 to second in set (it adds a function used in subsequent
     patch). It add a new function which is used on code refactoring.

  3. Split the 12/20 a patch to do *just* the refactor that uses the
     _dl_find_dso and move the RTLD_ISOLATE to the patch that actually 
     enables RTLD_SHARED.

  4. Add a patch to add the DT_GNU_FLAGS_1 dynamic tag on the required
     library (it should be safer since there is no logic yet that consumes it).

  5. Combine all the remaining patch that enable RTLD_SHARED and RT_ISOLATE
     on a single patch. It would be large patch, but it is more logically
     consistent and easier to revert or backport.

  6. Add the tests.