diff mbox series

[v2,2/2] Extend struct r_debug to support multiple namespaces

Message ID 20210817010629.593479-3-hjl.tools@gmail.com
State Superseded
Headers show
Series Extend struct r_debug to support multiple namespaces | expand

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent
dj/TryBot-32bit success Build for i686

Commit Message

H.J. Lu Aug. 17, 2021, 1:06 a.m. UTC
Glibc does not provide an interface for debugger to access libraries
loaded in multiple namespaces via dlmopen.

The current rtld-debugger interface is described in the file:

elf/rtld-debugger-interface.txt

under the "Standard debugger interface" heading.  This interface only
provides access to the first link-map (LM_ID_BASE).

Based on the patch from Conan C Huang <conhuang@cisco.com>:

https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html

1. Bump r_version to 2.  This triggers the GDB bug:

https://sourceware.org/bugzilla/show_bug.cgi?id=28236

2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
where each element correlates to an unique namespace.
3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
as an alise of _r_debug_extended, for programs which reference _r_debug.
---
 csu/Makefile                    |  3 ++
 csu/rtld-sizes.sym              |  4 ++
 elf/Makefile                    | 22 ++++++++++-
 elf/dl-close.c                  |  2 +-
 elf/dl-debug-symbols-gen.c      | 24 +++++++++++
 elf/dl-debug-symbols.S          | 31 +++++++++++++++
 elf/dl-debug.c                  | 29 +++++++-------
 elf/dl-load.c                   |  2 +-
 elf/dl-open.c                   |  2 +-
 elf/dl-reloc-static-pie.c       |  2 +-
 elf/link.h                      | 70 +++++++++++++++++++++++++--------
 elf/rtld-debugger-interface.txt | 14 +++++++
 elf/rtld.c                      |  4 +-
 elf/tst-dlmopen4.c              | 68 ++++++++++++++++++++++++++++++++
 include/link.h                  |  4 ++
 sysdeps/generic/ldsodefs.h      |  5 ++-
 16 files changed, 245 insertions(+), 41 deletions(-)
 create mode 100644 csu/rtld-sizes.sym
 create mode 100644 elf/dl-debug-symbols-gen.c
 create mode 100644 elf/dl-debug-symbols.S
 create mode 100644 elf/tst-dlmopen4.c

Comments

Daniel Walker Aug. 17, 2021, 5:44 p.m. UTC | #1
On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> Glibc does not provide an interface for debugger to access libraries
> loaded in multiple namespaces via dlmopen.
> 
> The current rtld-debugger interface is described in the file:
> 
> elf/rtld-debugger-interface.txt
> 
> under the "Standard debugger interface" heading.  This interface only
> provides access to the first link-map (LM_ID_BASE).
> 
> Based on the patch from Conan C Huang <conhuang@cisco.com>:
> 
> https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> 
> 1. Bump r_version to 2.  This triggers the GDB bug:
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> 
> 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> where each element correlates to an unique namespace.
> 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> as an alise of _r_debug_extended, for programs which reference _r_debug.

It's very similar to patches we have already created internally.

One comment below,

> ---
>  csu/Makefile                    |  3 ++
>  csu/rtld-sizes.sym              |  4 ++
>  elf/Makefile                    | 22 ++++++++++-
>  elf/dl-close.c                  |  2 +-
>  elf/dl-debug-symbols-gen.c      | 24 +++++++++++
>  elf/dl-debug-symbols.S          | 31 +++++++++++++++
>  elf/dl-debug.c                  | 29 +++++++-------
>  elf/dl-load.c                   |  2 +-
>  elf/dl-open.c                   |  2 +-
>  elf/dl-reloc-static-pie.c       |  2 +-
>  elf/link.h                      | 70 +++++++++++++++++++++++++--------
>  elf/rtld-debugger-interface.txt | 14 +++++++
>  elf/rtld.c                      |  4 +-
>  elf/tst-dlmopen4.c              | 68 ++++++++++++++++++++++++++++++++
>  include/link.h                  |  4 ++
>  sysdeps/generic/ldsodefs.h      |  5 ++-
>  16 files changed, 245 insertions(+), 41 deletions(-)
>  create mode 100644 csu/rtld-sizes.sym
>  create mode 100644 elf/dl-debug-symbols-gen.c
>  create mode 100644 elf/dl-debug-symbols.S
>  create mode 100644 elf/tst-dlmopen4.c
> 
> diff --git a/csu/Makefile b/csu/Makefile
> index 3054329cea..e2390e4a7d 100644
> --- a/csu/Makefile
> +++ b/csu/Makefile
> @@ -88,6 +88,9 @@ endif
>  before-compile += $(objpfx)abi-tag.h
>  generated += abi-tag.h
>  
> +# Put it here to generate it earlier.
> +gen-as-const-headers += rtld-sizes.sym
> +
>  # These are the special initializer/finalizer files.  They are always the
>  # first and last file in the link.  crti.o ... crtn.o define the global
>  # "functions" _init and _fini to run the .init and .fini sections.
> diff --git a/csu/rtld-sizes.sym b/csu/rtld-sizes.sym
> new file mode 100644
> index 0000000000..40dd8edaec
> --- /dev/null
> +++ b/csu/rtld-sizes.sym
> @@ -0,0 +1,4 @@
> +#include <link.h>
> +
> +--
> +COMPAT_R_DEBUG_SIZE	sizeof (struct r_debug)
> diff --git a/elf/Makefile b/elf/Makefile
> index 725537c40b..1444a53405 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -35,7 +35,8 @@ dl-routines	= $(addprefix dl-,load lookup object reloc deps \
>  				  execstack open close trampoline \
>  				  exception sort-maps lookup-direct \
>  				  call-libc-early-init write \
> -				  thread_gscope_wait tls_init_tp)
> +				  thread_gscope_wait tls_init_tp \
> +				  debug-symbols)
>  ifeq (yes,$(use-ldconfig))
>  dl-routines += dl-cache
>  endif
> @@ -203,7 +204,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
>  	 tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \
>  	 tst-align tst-align2 \
>  	 tst-dlmodcount tst-dlopenrpath tst-deep1 \
> -	 tst-dlmopen1 tst-dlmopen3 \
> +	 tst-dlmopen1 tst-dlmopen3 tst-dlmopen4 \
>  	 unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \
>  	 tst-audit1 tst-audit2 tst-audit8 tst-audit9 \
>  	 tst-addr1 tst-thrlock \
> @@ -672,6 +673,21 @@ LC_ALL=C sed $(ldd-rewrite) < $< \
>  endef
>  endif
>  
> +ifeq ($(build-shared),yes)
> +generated += dl-debug-compat-symbols.os dl-debug-compat-symbols.o
> +
> +libof-dl-debug-compat-symbols = rtld
> +
> +$(objpfx)dl-debug-compat-symbols.os: dl-debug-symbols-gen.c
> +	$(compile-command.c) -S
> +
> +$(objpfx)dl-debug-compat-symbols.o: dl-debug-symbols-gen.c
> +	$(compile-command.c) -S
> +
> +$(objpfx)dl-debug-symbols.os: $(objpfx)dl-debug-compat-symbols.os
> +$(objpfx)dl-debug-symbols.o: $(objpfx)dl-debug-compat-symbols.o
> +endif
> +
>  $(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
>  	      $(common-objpfx)config.make
>  	$(gen-ldd)
> @@ -1242,6 +1258,8 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
>  
>  $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
>  
> +$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so
> +
>  $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
>  tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
>  
> diff --git a/elf/dl-close.c b/elf/dl-close.c
> index f39001cab9..f59ffdd666 100644
> --- a/elf/dl-close.c
> +++ b/elf/dl-close.c
> @@ -500,7 +500,7 @@ _dl_close_worker (struct link_map *map, bool force)
>  #endif
>  
>    /* Notify the debugger we are about to remove some loaded objects.  */
> -  struct r_debug *r = _dl_debug_initialize (0, nsid);
> +  struct r_debug_extended *r = _dl_debug_initialize (0, nsid);
>    r->r_state = RT_DELETE;
>    _dl_debug_state ();
>    LIBC_PROBE (unmap_start, 2, nsid, r);
> diff --git a/elf/dl-debug-symbols-gen.c b/elf/dl-debug-symbols-gen.c
> new file mode 100644
> index 0000000000..2406260bcb
> --- /dev/null
> +++ b/elf/dl-debug-symbols-gen.c
> @@ -0,0 +1,24 @@
> +/* Generate the _r_debug_extended symbol used to communicate dynamic
> +   linker state to the debugger at runtime.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <link.h>
> +
> +/* This structure communicates dl state to the debugger.  The debugger
> +   finds it via the DT_DEBUG entry in the dynamic section.  */
> +struct r_debug_extended _r_debug_extended;
> diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
> new file mode 100644
> index 0000000000..0966b172ab
> --- /dev/null
> +++ b/elf/dl-debug-symbols.S
> @@ -0,0 +1,31 @@
> +/* Define symbols used to communicate dynamic linker state to the
> +   debugger at runtime.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <rtld-sizes.h>
> +
> +#ifdef SHARED
> +# include "dl-debug-compat-symbols.os"
> +#else
> +# include "dl-debug-compat-symbols.o"
> +#endif
> +
> +/* Define the compatibility symbol, _r_debug, with size of struct r_debug,
> +   as an alias of _r_debug_extended.  */
> +declare_object_symbol_alias (_r_debug, _r_debug_extended,
> +			     COMPAT_R_DEBUG_SIZE);
> diff --git a/elf/dl-debug.c b/elf/dl-debug.c
> index 2cd5f09753..9e884a5648 100644
> --- a/elf/dl-debug.c
> +++ b/elf/dl-debug.c
> @@ -30,34 +30,35 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
>  					  && VERIFY_MEMBER (l_prev))
>  					 ? 1 : -1];
>  
> -/* This structure communicates dl state to the debugger.  The debugger
> -   normally finds it via the DT_DEBUG entry in the dynamic section, but in
> -   a statically-linked program there is no dynamic section for the debugger
> -   to examine and it looks for this particular symbol name.  */
> -struct r_debug _r_debug;
> -
> -
>  /* Initialize _r_debug if it has not already been done.  The argument is
>     the run-time load address of the dynamic linker, to be put in
>     _r_debug.r_ldbase.  Returns the address of _r_debug.  */
>  
> -struct r_debug *
> +struct r_debug_extended *
>  _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
>  {
> -  struct r_debug *r;
> +  struct r_debug_extended *r, *rp;
>  
>    if (ns == LM_ID_BASE)
> -    r = &_r_debug;
> +    r = &_r_debug_extended;
>    else
> -    r = &GL(dl_ns)[ns]._ns_debug;
> +    {
> +      r = &GL(dl_ns)[ns]._ns_debug;
> +      rp = &GL(dl_ns)[ns - 1]._ns_debug;
> +      rp->r_next = r;
> +      if (ns - 1 == LM_ID_BASE)
> +        _r_debug_extended.r_next = r;
> +    }

I'm not sure why, but we have an issue assigning the dl_ns[ns - 1] when ns -1
was equal to LM_ID_BASE. I don't know what the issue was but we had tests fail
as a result of it. In my case I add an else clause and only set r_next when it
wasn't LM_ID_BASE, and for debugging it didn't matter since the structure was
all that mattered.

Daniel
Daniel Walker Aug. 17, 2021, 5:57 p.m. UTC | #2
On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> Glibc does not provide an interface for debugger to access libraries
> loaded in multiple namespaces via dlmopen.
> 
> The current rtld-debugger interface is described in the file:
> 
> elf/rtld-debugger-interface.txt
> 
> under the "Standard debugger interface" heading.  This interface only
> provides access to the first link-map (LM_ID_BASE).
> 
> Based on the patch from Conan C Huang <conhuang@cisco.com>:
> 
> https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> 
> 1. Bump r_version to 2.  This triggers the GDB bug:
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> 
> 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> where each element correlates to an unique namespace.
> 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> as an alise of _r_debug_extended, for programs which reference _r_debug.


I've attached the GDB patch which was created at Cisco to support our version of
this type of feature which is similar to what you've created. You might be able
to make some small modifications to make it work for you.

Daniel
H.J. Lu Aug. 17, 2021, 8:25 p.m. UTC | #3
On Tue, Aug 17, 2021 at 10:44 AM Daniel Walker <danielwa@cisco.com> wrote:
>
> On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> > Glibc does not provide an interface for debugger to access libraries
> > loaded in multiple namespaces via dlmopen.
> >
> > The current rtld-debugger interface is described in the file:
> >
> > elf/rtld-debugger-interface.txt
> >
> > under the "Standard debugger interface" heading.  This interface only
> > provides access to the first link-map (LM_ID_BASE).
> >
> > Based on the patch from Conan C Huang <conhuang@cisco.com>:
> >
> > https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> >
> > 1. Bump r_version to 2.  This triggers the GDB bug:
> >
> > https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> >
> > 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> > where each element correlates to an unique namespace.
> > 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> > 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> > as an alise of _r_debug_extended, for programs which reference _r_debug.
>
> It's very similar to patches we have already created internally.
>
> One comment below,
>
> > ---
> >  csu/Makefile                    |  3 ++
> >  csu/rtld-sizes.sym              |  4 ++
> >  elf/Makefile                    | 22 ++++++++++-
> >  elf/dl-close.c                  |  2 +-
> >  elf/dl-debug-symbols-gen.c      | 24 +++++++++++
> >  elf/dl-debug-symbols.S          | 31 +++++++++++++++
> >  elf/dl-debug.c                  | 29 +++++++-------
> >  elf/dl-load.c                   |  2 +-
> >  elf/dl-open.c                   |  2 +-
> >  elf/dl-reloc-static-pie.c       |  2 +-
> >  elf/link.h                      | 70 +++++++++++++++++++++++++--------
> >  elf/rtld-debugger-interface.txt | 14 +++++++
> >  elf/rtld.c                      |  4 +-
> >  elf/tst-dlmopen4.c              | 68 ++++++++++++++++++++++++++++++++
> >  include/link.h                  |  4 ++
> >  sysdeps/generic/ldsodefs.h      |  5 ++-
> >  16 files changed, 245 insertions(+), 41 deletions(-)
> >  create mode 100644 csu/rtld-sizes.sym
> >  create mode 100644 elf/dl-debug-symbols-gen.c
> >  create mode 100644 elf/dl-debug-symbols.S
> >  create mode 100644 elf/tst-dlmopen4.c
> >
> > diff --git a/csu/Makefile b/csu/Makefile
> > index 3054329cea..e2390e4a7d 100644
> > --- a/csu/Makefile
> > +++ b/csu/Makefile
> > @@ -88,6 +88,9 @@ endif
> >  before-compile += $(objpfx)abi-tag.h
> >  generated += abi-tag.h
> >
> > +# Put it here to generate it earlier.
> > +gen-as-const-headers += rtld-sizes.sym
> > +
> >  # These are the special initializer/finalizer files.  They are always the
> >  # first and last file in the link.  crti.o ... crtn.o define the global
> >  # "functions" _init and _fini to run the .init and .fini sections.
> > diff --git a/csu/rtld-sizes.sym b/csu/rtld-sizes.sym
> > new file mode 100644
> > index 0000000000..40dd8edaec
> > --- /dev/null
> > +++ b/csu/rtld-sizes.sym
> > @@ -0,0 +1,4 @@
> > +#include <link.h>
> > +
> > +--
> > +COMPAT_R_DEBUG_SIZE  sizeof (struct r_debug)
> > diff --git a/elf/Makefile b/elf/Makefile
> > index 725537c40b..1444a53405 100644
> > --- a/elf/Makefile
> > +++ b/elf/Makefile
> > @@ -35,7 +35,8 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps \
> >                                 execstack open close trampoline \
> >                                 exception sort-maps lookup-direct \
> >                                 call-libc-early-init write \
> > -                               thread_gscope_wait tls_init_tp)
> > +                               thread_gscope_wait tls_init_tp \
> > +                               debug-symbols)
> >  ifeq (yes,$(use-ldconfig))
> >  dl-routines += dl-cache
> >  endif
> > @@ -203,7 +204,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
> >        tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \
> >        tst-align tst-align2 \
> >        tst-dlmodcount tst-dlopenrpath tst-deep1 \
> > -      tst-dlmopen1 tst-dlmopen3 \
> > +      tst-dlmopen1 tst-dlmopen3 tst-dlmopen4 \
> >        unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \
> >        tst-audit1 tst-audit2 tst-audit8 tst-audit9 \
> >        tst-addr1 tst-thrlock \
> > @@ -672,6 +673,21 @@ LC_ALL=C sed $(ldd-rewrite) < $< \
> >  endef
> >  endif
> >
> > +ifeq ($(build-shared),yes)
> > +generated += dl-debug-compat-symbols.os dl-debug-compat-symbols.o
> > +
> > +libof-dl-debug-compat-symbols = rtld
> > +
> > +$(objpfx)dl-debug-compat-symbols.os: dl-debug-symbols-gen.c
> > +     $(compile-command.c) -S
> > +
> > +$(objpfx)dl-debug-compat-symbols.o: dl-debug-symbols-gen.c
> > +     $(compile-command.c) -S
> > +
> > +$(objpfx)dl-debug-symbols.os: $(objpfx)dl-debug-compat-symbols.os
> > +$(objpfx)dl-debug-symbols.o: $(objpfx)dl-debug-compat-symbols.o
> > +endif
> > +
> >  $(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
> >             $(common-objpfx)config.make
> >       $(gen-ldd)
> > @@ -1242,6 +1258,8 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
> >
> >  $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
> >
> > +$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so
> > +
> >  $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
> >  tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
> >
> > diff --git a/elf/dl-close.c b/elf/dl-close.c
> > index f39001cab9..f59ffdd666 100644
> > --- a/elf/dl-close.c
> > +++ b/elf/dl-close.c
> > @@ -500,7 +500,7 @@ _dl_close_worker (struct link_map *map, bool force)
> >  #endif
> >
> >    /* Notify the debugger we are about to remove some loaded objects.  */
> > -  struct r_debug *r = _dl_debug_initialize (0, nsid);
> > +  struct r_debug_extended *r = _dl_debug_initialize (0, nsid);
> >    r->r_state = RT_DELETE;
> >    _dl_debug_state ();
> >    LIBC_PROBE (unmap_start, 2, nsid, r);
> > diff --git a/elf/dl-debug-symbols-gen.c b/elf/dl-debug-symbols-gen.c
> > new file mode 100644
> > index 0000000000..2406260bcb
> > --- /dev/null
> > +++ b/elf/dl-debug-symbols-gen.c
> > @@ -0,0 +1,24 @@
> > +/* Generate the _r_debug_extended symbol used to communicate dynamic
> > +   linker state to the debugger at runtime.
> > +   Copyright (C) 2021 Free Software Foundation, Inc.
> > +   This file is part of the GNU C Library.
> > +
> > +   The GNU C Library is free software; you can redistribute it and/or
> > +   modify it under the terms of the GNU Lesser General Public
> > +   License as published by the Free Software Foundation; either
> > +   version 2.1 of the License, or (at your option) any later version.
> > +
> > +   The GNU C Library is distributed in the hope that it will be useful,
> > +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > +   Lesser General Public License for more details.
> > +
> > +   You should have received a copy of the GNU Lesser General Public
> > +   License along with the GNU C Library; if not, see
> > +   <https://www.gnu.org/licenses/>.  */
> > +
> > +#include <link.h>
> > +
> > +/* This structure communicates dl state to the debugger.  The debugger
> > +   finds it via the DT_DEBUG entry in the dynamic section.  */
> > +struct r_debug_extended _r_debug_extended;
> > diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
> > new file mode 100644
> > index 0000000000..0966b172ab
> > --- /dev/null
> > +++ b/elf/dl-debug-symbols.S
> > @@ -0,0 +1,31 @@
> > +/* Define symbols used to communicate dynamic linker state to the
> > +   debugger at runtime.
> > +   Copyright (C) 2021 Free Software Foundation, Inc.
> > +   This file is part of the GNU C Library.
> > +
> > +   The GNU C Library is free software; you can redistribute it and/or
> > +   modify it under the terms of the GNU Lesser General Public
> > +   License as published by the Free Software Foundation; either
> > +   version 2.1 of the License, or (at your option) any later version.
> > +
> > +   The GNU C Library is distributed in the hope that it will be useful,
> > +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > +   Lesser General Public License for more details.
> > +
> > +   You should have received a copy of the GNU Lesser General Public
> > +   License along with the GNU C Library; if not, see
> > +   <https://www.gnu.org/licenses/>.  */
> > +
> > +#include <rtld-sizes.h>
> > +
> > +#ifdef SHARED
> > +# include "dl-debug-compat-symbols.os"
> > +#else
> > +# include "dl-debug-compat-symbols.o"
> > +#endif
> > +
> > +/* Define the compatibility symbol, _r_debug, with size of struct r_debug,
> > +   as an alias of _r_debug_extended.  */
> > +declare_object_symbol_alias (_r_debug, _r_debug_extended,
> > +                          COMPAT_R_DEBUG_SIZE);
> > diff --git a/elf/dl-debug.c b/elf/dl-debug.c
> > index 2cd5f09753..9e884a5648 100644
> > --- a/elf/dl-debug.c
> > +++ b/elf/dl-debug.c
> > @@ -30,34 +30,35 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
> >                                         && VERIFY_MEMBER (l_prev))
> >                                        ? 1 : -1];
> >
> > -/* This structure communicates dl state to the debugger.  The debugger
> > -   normally finds it via the DT_DEBUG entry in the dynamic section, but in
> > -   a statically-linked program there is no dynamic section for the debugger
> > -   to examine and it looks for this particular symbol name.  */
> > -struct r_debug _r_debug;
> > -
> > -
> >  /* Initialize _r_debug if it has not already been done.  The argument is
> >     the run-time load address of the dynamic linker, to be put in
> >     _r_debug.r_ldbase.  Returns the address of _r_debug.  */
> >
> > -struct r_debug *
> > +struct r_debug_extended *
> >  _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
> >  {
> > -  struct r_debug *r;
> > +  struct r_debug_extended *r, *rp;
> >
> >    if (ns == LM_ID_BASE)
> > -    r = &_r_debug;
> > +    r = &_r_debug_extended;
> >    else
> > -    r = &GL(dl_ns)[ns]._ns_debug;
> > +    {
> > +      r = &GL(dl_ns)[ns]._ns_debug;
> > +      rp = &GL(dl_ns)[ns - 1]._ns_debug;
> > +      rp->r_next = r;
> > +      if (ns - 1 == LM_ID_BASE)
> > +        _r_debug_extended.r_next = r;
> > +    }
>
> I'm not sure why, but we have an issue assigning the dl_ns[ns - 1] when ns -1
> was equal to LM_ID_BASE. I don't know what the issue was but we had tests fail
> as a result of it. In my case I add an else clause and only set r_next when it
> wasn't LM_ID_BASE, and for debugging it didn't matter since the structure was
> all that mattered.
>
> Daniel

_dl_debug_initialize can be called multiple times like

 _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;

I will update my patch to fix it.
H.J. Lu Aug. 17, 2021, 8:26 p.m. UTC | #4
On Tue, Aug 17, 2021 at 10:57 AM Daniel Walker <danielwa@cisco.com> wrote:
>
> On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> > Glibc does not provide an interface for debugger to access libraries
> > loaded in multiple namespaces via dlmopen.
> >
> > The current rtld-debugger interface is described in the file:
> >
> > elf/rtld-debugger-interface.txt
> >
> > under the "Standard debugger interface" heading.  This interface only
> > provides access to the first link-map (LM_ID_BASE).
> >
> > Based on the patch from Conan C Huang <conhuang@cisco.com>:
> >
> > https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> >
> > 1. Bump r_version to 2.  This triggers the GDB bug:
> >
> > https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> >
> > 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> > where each element correlates to an unique namespace.
> > 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> > 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> > as an alise of _r_debug_extended, for programs which reference _r_debug.
>
>
> I've attached the GDB patch which was created at Cisco to support our version of
> this type of feature which is similar to what you've created. You might be able
> to make some small modifications to make it work for you.
>
> Daniel

I will give it a try.

Thanks.
H.J. Lu Aug. 18, 2021, 12:14 a.m. UTC | #5
On Tue, Aug 17, 2021 at 1:25 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Aug 17, 2021 at 10:44 AM Daniel Walker <danielwa@cisco.com> wrote:
> >
> > On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> > > Glibc does not provide an interface for debugger to access libraries
> > > loaded in multiple namespaces via dlmopen.
> > >
> > > The current rtld-debugger interface is described in the file:
> > >
> > > elf/rtld-debugger-interface.txt
> > >
> > > under the "Standard debugger interface" heading.  This interface only
> > > provides access to the first link-map (LM_ID_BASE).
> > >
> > > Based on the patch from Conan C Huang <conhuang@cisco.com>:
> > >
> > > https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> > >
> > > 1. Bump r_version to 2.  This triggers the GDB bug:
> > >
> > > https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> > >
> > > 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> > > where each element correlates to an unique namespace.
> > > 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> > > 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> > > as an alise of _r_debug_extended, for programs which reference _r_debug.
> >
> > It's very similar to patches we have already created internally.
> >
> > One comment below,
> >
> > > ---
> > >  csu/Makefile                    |  3 ++
> > >  csu/rtld-sizes.sym              |  4 ++
> > >  elf/Makefile                    | 22 ++++++++++-
> > >  elf/dl-close.c                  |  2 +-
> > >  elf/dl-debug-symbols-gen.c      | 24 +++++++++++
> > >  elf/dl-debug-symbols.S          | 31 +++++++++++++++
> > >  elf/dl-debug.c                  | 29 +++++++-------
> > >  elf/dl-load.c                   |  2 +-
> > >  elf/dl-open.c                   |  2 +-
> > >  elf/dl-reloc-static-pie.c       |  2 +-
> > >  elf/link.h                      | 70 +++++++++++++++++++++++++--------
> > >  elf/rtld-debugger-interface.txt | 14 +++++++
> > >  elf/rtld.c                      |  4 +-
> > >  elf/tst-dlmopen4.c              | 68 ++++++++++++++++++++++++++++++++
> > >  include/link.h                  |  4 ++
> > >  sysdeps/generic/ldsodefs.h      |  5 ++-
> > >  16 files changed, 245 insertions(+), 41 deletions(-)
> > >  create mode 100644 csu/rtld-sizes.sym
> > >  create mode 100644 elf/dl-debug-symbols-gen.c
> > >  create mode 100644 elf/dl-debug-symbols.S
> > >  create mode 100644 elf/tst-dlmopen4.c
> > >
> > > diff --git a/csu/Makefile b/csu/Makefile
> > > index 3054329cea..e2390e4a7d 100644
> > > --- a/csu/Makefile
> > > +++ b/csu/Makefile
> > > @@ -88,6 +88,9 @@ endif
> > >  before-compile += $(objpfx)abi-tag.h
> > >  generated += abi-tag.h
> > >
> > > +# Put it here to generate it earlier.
> > > +gen-as-const-headers += rtld-sizes.sym
> > > +
> > >  # These are the special initializer/finalizer files.  They are always the
> > >  # first and last file in the link.  crti.o ... crtn.o define the global
> > >  # "functions" _init and _fini to run the .init and .fini sections.
> > > diff --git a/csu/rtld-sizes.sym b/csu/rtld-sizes.sym
> > > new file mode 100644
> > > index 0000000000..40dd8edaec
> > > --- /dev/null
> > > +++ b/csu/rtld-sizes.sym
> > > @@ -0,0 +1,4 @@
> > > +#include <link.h>
> > > +
> > > +--
> > > +COMPAT_R_DEBUG_SIZE  sizeof (struct r_debug)
> > > diff --git a/elf/Makefile b/elf/Makefile
> > > index 725537c40b..1444a53405 100644
> > > --- a/elf/Makefile
> > > +++ b/elf/Makefile
> > > @@ -35,7 +35,8 @@ dl-routines = $(addprefix dl-,load lookup object reloc deps \
> > >                                 execstack open close trampoline \
> > >                                 exception sort-maps lookup-direct \
> > >                                 call-libc-early-init write \
> > > -                               thread_gscope_wait tls_init_tp)
> > > +                               thread_gscope_wait tls_init_tp \
> > > +                               debug-symbols)
> > >  ifeq (yes,$(use-ldconfig))
> > >  dl-routines += dl-cache
> > >  endif
> > > @@ -203,7 +204,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
> > >        tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \
> > >        tst-align tst-align2 \
> > >        tst-dlmodcount tst-dlopenrpath tst-deep1 \
> > > -      tst-dlmopen1 tst-dlmopen3 \
> > > +      tst-dlmopen1 tst-dlmopen3 tst-dlmopen4 \
> > >        unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \
> > >        tst-audit1 tst-audit2 tst-audit8 tst-audit9 \
> > >        tst-addr1 tst-thrlock \
> > > @@ -672,6 +673,21 @@ LC_ALL=C sed $(ldd-rewrite) < $< \
> > >  endef
> > >  endif
> > >
> > > +ifeq ($(build-shared),yes)
> > > +generated += dl-debug-compat-symbols.os dl-debug-compat-symbols.o
> > > +
> > > +libof-dl-debug-compat-symbols = rtld
> > > +
> > > +$(objpfx)dl-debug-compat-symbols.os: dl-debug-symbols-gen.c
> > > +     $(compile-command.c) -S
> > > +
> > > +$(objpfx)dl-debug-compat-symbols.o: dl-debug-symbols-gen.c
> > > +     $(compile-command.c) -S
> > > +
> > > +$(objpfx)dl-debug-symbols.os: $(objpfx)dl-debug-compat-symbols.os
> > > +$(objpfx)dl-debug-symbols.o: $(objpfx)dl-debug-compat-symbols.o
> > > +endif
> > > +
> > >  $(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
> > >             $(common-objpfx)config.make
> > >       $(gen-ldd)
> > > @@ -1242,6 +1258,8 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
> > >
> > >  $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
> > >
> > > +$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so
> > > +
> > >  $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
> > >  tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
> > >
> > > diff --git a/elf/dl-close.c b/elf/dl-close.c
> > > index f39001cab9..f59ffdd666 100644
> > > --- a/elf/dl-close.c
> > > +++ b/elf/dl-close.c
> > > @@ -500,7 +500,7 @@ _dl_close_worker (struct link_map *map, bool force)
> > >  #endif
> > >
> > >    /* Notify the debugger we are about to remove some loaded objects.  */
> > > -  struct r_debug *r = _dl_debug_initialize (0, nsid);
> > > +  struct r_debug_extended *r = _dl_debug_initialize (0, nsid);
> > >    r->r_state = RT_DELETE;
> > >    _dl_debug_state ();
> > >    LIBC_PROBE (unmap_start, 2, nsid, r);
> > > diff --git a/elf/dl-debug-symbols-gen.c b/elf/dl-debug-symbols-gen.c
> > > new file mode 100644
> > > index 0000000000..2406260bcb
> > > --- /dev/null
> > > +++ b/elf/dl-debug-symbols-gen.c
> > > @@ -0,0 +1,24 @@
> > > +/* Generate the _r_debug_extended symbol used to communicate dynamic
> > > +   linker state to the debugger at runtime.
> > > +   Copyright (C) 2021 Free Software Foundation, Inc.
> > > +   This file is part of the GNU C Library.
> > > +
> > > +   The GNU C Library is free software; you can redistribute it and/or
> > > +   modify it under the terms of the GNU Lesser General Public
> > > +   License as published by the Free Software Foundation; either
> > > +   version 2.1 of the License, or (at your option) any later version.
> > > +
> > > +   The GNU C Library is distributed in the hope that it will be useful,
> > > +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > > +   Lesser General Public License for more details.
> > > +
> > > +   You should have received a copy of the GNU Lesser General Public
> > > +   License along with the GNU C Library; if not, see
> > > +   <https://www.gnu.org/licenses/>.  */
> > > +
> > > +#include <link.h>
> > > +
> > > +/* This structure communicates dl state to the debugger.  The debugger
> > > +   finds it via the DT_DEBUG entry in the dynamic section.  */
> > > +struct r_debug_extended _r_debug_extended;
> > > diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
> > > new file mode 100644
> > > index 0000000000..0966b172ab
> > > --- /dev/null
> > > +++ b/elf/dl-debug-symbols.S
> > > @@ -0,0 +1,31 @@
> > > +/* Define symbols used to communicate dynamic linker state to the
> > > +   debugger at runtime.
> > > +   Copyright (C) 2021 Free Software Foundation, Inc.
> > > +   This file is part of the GNU C Library.
> > > +
> > > +   The GNU C Library is free software; you can redistribute it and/or
> > > +   modify it under the terms of the GNU Lesser General Public
> > > +   License as published by the Free Software Foundation; either
> > > +   version 2.1 of the License, or (at your option) any later version.
> > > +
> > > +   The GNU C Library is distributed in the hope that it will be useful,
> > > +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > > +   Lesser General Public License for more details.
> > > +
> > > +   You should have received a copy of the GNU Lesser General Public
> > > +   License along with the GNU C Library; if not, see
> > > +   <https://www.gnu.org/licenses/>.  */
> > > +
> > > +#include <rtld-sizes.h>
> > > +
> > > +#ifdef SHARED
> > > +# include "dl-debug-compat-symbols.os"
> > > +#else
> > > +# include "dl-debug-compat-symbols.o"
> > > +#endif
> > > +
> > > +/* Define the compatibility symbol, _r_debug, with size of struct r_debug,
> > > +   as an alias of _r_debug_extended.  */
> > > +declare_object_symbol_alias (_r_debug, _r_debug_extended,
> > > +                          COMPAT_R_DEBUG_SIZE);
> > > diff --git a/elf/dl-debug.c b/elf/dl-debug.c
> > > index 2cd5f09753..9e884a5648 100644
> > > --- a/elf/dl-debug.c
> > > +++ b/elf/dl-debug.c
> > > @@ -30,34 +30,35 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
> > >                                         && VERIFY_MEMBER (l_prev))
> > >                                        ? 1 : -1];
> > >
> > > -/* This structure communicates dl state to the debugger.  The debugger
> > > -   normally finds it via the DT_DEBUG entry in the dynamic section, but in
> > > -   a statically-linked program there is no dynamic section for the debugger
> > > -   to examine and it looks for this particular symbol name.  */
> > > -struct r_debug _r_debug;
> > > -
> > > -
> > >  /* Initialize _r_debug if it has not already been done.  The argument is
> > >     the run-time load address of the dynamic linker, to be put in
> > >     _r_debug.r_ldbase.  Returns the address of _r_debug.  */
> > >
> > > -struct r_debug *
> > > +struct r_debug_extended *
> > >  _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
> > >  {
> > > -  struct r_debug *r;
> > > +  struct r_debug_extended *r, *rp;
> > >
> > >    if (ns == LM_ID_BASE)
> > > -    r = &_r_debug;
> > > +    r = &_r_debug_extended;
> > >    else
> > > -    r = &GL(dl_ns)[ns]._ns_debug;
> > > +    {
> > > +      r = &GL(dl_ns)[ns]._ns_debug;
> > > +      rp = &GL(dl_ns)[ns - 1]._ns_debug;
> > > +      rp->r_next = r;
> > > +      if (ns - 1 == LM_ID_BASE)
> > > +        _r_debug_extended.r_next = r;
> > > +    }
> >
> > I'm not sure why, but we have an issue assigning the dl_ns[ns - 1] when ns -1
> > was equal to LM_ID_BASE. I don't know what the issue was but we had tests fail
> > as a result of it. In my case I add an else clause and only set r_next when it
> > wasn't LM_ID_BASE, and for debugging it didn't matter since the structure was
> > all that mattered.
> >
> > Daniel
>
> _dl_debug_initialize can be called multiple times like
>
>  _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
>
> I will update my patch to fix it.

I sent out the v3 patch to fix it.
H.J. Lu Aug. 18, 2021, 12:21 a.m. UTC | #6
On Tue, Aug 17, 2021 at 1:26 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Aug 17, 2021 at 10:57 AM Daniel Walker <danielwa@cisco.com> wrote:
> >
> > On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> > > Glibc does not provide an interface for debugger to access libraries
> > > loaded in multiple namespaces via dlmopen.
> > >
> > > The current rtld-debugger interface is described in the file:
> > >
> > > elf/rtld-debugger-interface.txt
> > >
> > > under the "Standard debugger interface" heading.  This interface only
> > > provides access to the first link-map (LM_ID_BASE).
> > >
> > > Based on the patch from Conan C Huang <conhuang@cisco.com>:
> > >
> > > https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> > >
> > > 1. Bump r_version to 2.  This triggers the GDB bug:
> > >
> > > https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> > >
> > > 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> > > where each element correlates to an unique namespace.
> > > 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> > > 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> > > as an alise of _r_debug_extended, for programs which reference _r_debug.
> >
> >
> > I've attached the GDB patch which was created at Cisco to support our version of
> > this type of feature which is similar to what you've created. You might be able
> > to make some small modifications to make it work for you.
> >
> > Daniel
>
>

It seems to work pretty well with some cleanup:

https://gitlab.com/x86-binutils/binutils-gdb/-/commit/ea336450f484fc8c0aa512a60bc99f69309a46d8

(gdb) set  stop-on-solib-events 1
(gdb) r --direct
Starting program:
/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen4
--direct
Stopped due to shared library event (no libraries added or removed)
(gdb) c
Continuing.
warning: Unable to find libthread_db matching inferior's thread
library, thread debugging will not be available.
Stopped due to shared library event:
  Inferior loaded
/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
(gdb)
Continuing.
Stopped due to shared library event (no libraries added or removed)
(gdb)
Continuing.
warning: .dynamic section for
"/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2"
is not at the expected address (wrong library or version mismatch?)
warning: Unable to find libthread_db matching inferior's thread
library, thread debugging will not be available.
Stopped due to shared library event:
  Inferior loaded
/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so
    /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
    /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2
(gdb)
Continuing.
Stopped due to shared library event (no libraries added or removed)
(gdb)
Continuing.
Stopped due to shared library event:
  Inferior unloaded
/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so
    /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
    /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2
(gdb)
Continuing.
[Inferior 1 (process 638559) exited normally]
(gdb)

(gdb) r --direct
Starting program:
/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen4
--direct
warning: Unable to find libthread_db matching inferior's thread
library, thread debugging will not be available.
warning: .dynamic section for
"/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2"
is not at the expected address (wrong library or version mismatch?)
warning: Unable to find libthread_db matching inferior's thread
library, thread debugging will not be available.
[Inferior 1 (process 638480) exited normally]
(gdb)

It looks like l_ld of ld.so in the new namespace isn't set properly:

(gdb) p debug->r_next->r_map
$5 = (struct link_map *) 0x408360
(gdb) p *debug->r_next->r_map
$6 = {l_addr = 140737351757824,
  l_name = 0x4082f0
"/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so",
l_ld = 0x7ffff7dbee10, l_next = 0x408900,
  l_prev = 0x0}
(gdb) p *debug->r_next->r_map->l_next
$7 = {l_addr = 140737349664768,
  l_name = 0x4088a0
"/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6",
l_ld = 0x7ffff7daabc0, l_next = 0x408e90, l_prev = 0x408360}
(gdb) p *debug->r_next->r_map->l_next->l_next
$8 = {l_addr = 0,
  l_name = 0x408e20
"/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2",
l_ld = 0x0, l_next = 0x0, l_prev = 0x408900}
(gdb)
H.J. Lu Aug. 18, 2021, 2:36 a.m. UTC | #7
On Tue, Aug 17, 2021 at 5:21 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Aug 17, 2021 at 1:26 PM H.J. Lu <hjl.tools@gmail.com> wrote:
> >
> > On Tue, Aug 17, 2021 at 10:57 AM Daniel Walker <danielwa@cisco.com> wrote:
> > >
> > > On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> > > > Glibc does not provide an interface for debugger to access libraries
> > > > loaded in multiple namespaces via dlmopen.
> > > >
> > > > The current rtld-debugger interface is described in the file:
> > > >
> > > > elf/rtld-debugger-interface.txt
> > > >
> > > > under the "Standard debugger interface" heading.  This interface only
> > > > provides access to the first link-map (LM_ID_BASE).
> > > >
> > > > Based on the patch from Conan C Huang <conhuang@cisco.com>:
> > > >
> > > > https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> > > >
> > > > 1. Bump r_version to 2.  This triggers the GDB bug:
> > > >
> > > > https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> > > >
> > > > 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> > > > where each element correlates to an unique namespace.
> > > > 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> > > > 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> > > > as an alise of _r_debug_extended, for programs which reference _r_debug.
> > >
> > >
> > > I've attached the GDB patch which was created at Cisco to support our version of
> > > this type of feature which is similar to what you've created. You might be able
> > > to make some small modifications to make it work for you.
> > >
> > > Daniel
> >
> >
>
> It seems to work pretty well with some cleanup:
>
> https://gitlab.com/x86-binutils/binutils-gdb/-/commit/ea336450f484fc8c0aa512a60bc99f69309a46d8
>
> (gdb) set  stop-on-solib-events 1
> (gdb) r --direct
> Starting program:
> /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen4
> --direct
> Stopped due to shared library event (no libraries added or removed)
> (gdb) c
> Continuing.
> warning: Unable to find libthread_db matching inferior's thread
> library, thread debugging will not be available.
> Stopped due to shared library event:
>   Inferior loaded
> /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
> (gdb)
> Continuing.
> Stopped due to shared library event (no libraries added or removed)
> (gdb)
> Continuing.
> warning: .dynamic section for
> "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2"
> is not at the expected address (wrong library or version mismatch?)
> warning: Unable to find libthread_db matching inferior's thread
> library, thread debugging will not be available.
> Stopped due to shared library event:
>   Inferior loaded
> /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so
>     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
>     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2
> (gdb)
> Continuing.
> Stopped due to shared library event (no libraries added or removed)
> (gdb)
> Continuing.
> Stopped due to shared library event:
>   Inferior unloaded
> /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so
>     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
>     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2
> (gdb)
> Continuing.
> [Inferior 1 (process 638559) exited normally]
> (gdb)
>
> (gdb) r --direct
> Starting program:
> /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen4
> --direct
> warning: Unable to find libthread_db matching inferior's thread
> library, thread debugging will not be available.
> warning: .dynamic section for
> "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2"
> is not at the expected address (wrong library or version mismatch?)
> warning: Unable to find libthread_db matching inferior's thread
> library, thread debugging will not be available.
> [Inferior 1 (process 638480) exited normally]
> (gdb)
>
> It looks like l_ld of ld.so in the new namespace isn't set properly:
>
> (gdb) p debug->r_next->r_map
> $5 = (struct link_map *) 0x408360
> (gdb) p *debug->r_next->r_map
> $6 = {l_addr = 140737351757824,
>   l_name = 0x4082f0
> "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so",
> l_ld = 0x7ffff7dbee10, l_next = 0x408900,
>   l_prev = 0x0}
> (gdb) p *debug->r_next->r_map->l_next
> $7 = {l_addr = 140737349664768,
>   l_name = 0x4088a0
> "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6",
> l_ld = 0x7ffff7daabc0, l_next = 0x408e90, l_prev = 0x408360}
> (gdb) p *debug->r_next->r_map->l_next->l_next
> $8 = {l_addr = 0,
>   l_name = 0x408e20
> "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2",
> l_ld = 0x0, l_next = 0x0, l_prev = 0x408900}
> (gdb)
>
>

Please try glibc users/hjl/pr15971/r_version branch:

https://gitlab.com/x86-glibc/glibc/-/tree/users/hjl/pr15971/r_version

and GDB users/hjl/pr11839/master branch:

https://gitlab.com/x86-binutils/binutils-gdb/-/tree/users/hjl/pr11839/master
H.J. Lu Aug. 18, 2021, 1:34 p.m. UTC | #8
On Tue, Aug 17, 2021 at 7:36 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Tue, Aug 17, 2021 at 5:21 PM H.J. Lu <hjl.tools@gmail.com> wrote:
> >
> > On Tue, Aug 17, 2021 at 1:26 PM H.J. Lu <hjl.tools@gmail.com> wrote:
> > >
> > > On Tue, Aug 17, 2021 at 10:57 AM Daniel Walker <danielwa@cisco.com> wrote:
> > > >
> > > > On Mon, Aug 16, 2021 at 06:06:29PM -0700, H.J. Lu wrote:
> > > > > Glibc does not provide an interface for debugger to access libraries
> > > > > loaded in multiple namespaces via dlmopen.
> > > > >
> > > > > The current rtld-debugger interface is described in the file:
> > > > >
> > > > > elf/rtld-debugger-interface.txt
> > > > >
> > > > > under the "Standard debugger interface" heading.  This interface only
> > > > > provides access to the first link-map (LM_ID_BASE).
> > > > >
> > > > > Based on the patch from Conan C Huang <conhuang@cisco.com>:
> > > > >
> > > > > https://sourceware.org/pipermail/libc-alpha/2020-June/115448.html
> > > > >
> > > > > 1. Bump r_version to 2.  This triggers the GDB bug:
> > > > >
> > > > > https://sourceware.org/bugzilla/show_bug.cgi?id=28236
> > > > >
> > > > > 2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
> > > > > where each element correlates to an unique namespace.
> > > > > 3. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
> > > > > 4. Provide the compatibility symbol, _r_debug, with size of struct r_debug,
> > > > > as an alise of _r_debug_extended, for programs which reference _r_debug.
> > > >
> > > >
> > > > I've attached the GDB patch which was created at Cisco to support our version of
> > > > this type of feature which is similar to what you've created. You might be able
> > > > to make some small modifications to make it work for you.
> > > >
> > > > Daniel
> > >
> > >
> >
> > It seems to work pretty well with some cleanup:
> >
> > https://gitlab.com/x86-binutils/binutils-gdb/-/commit/ea336450f484fc8c0aa512a60bc99f69309a46d8
> >
> > (gdb) set  stop-on-solib-events 1
> > (gdb) r --direct
> > Starting program:
> > /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen4
> > --direct
> > Stopped due to shared library event (no libraries added or removed)
> > (gdb) c
> > Continuing.
> > warning: Unable to find libthread_db matching inferior's thread
> > library, thread debugging will not be available.
> > Stopped due to shared library event:
> >   Inferior loaded
> > /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
> > (gdb)
> > Continuing.
> > Stopped due to shared library event (no libraries added or removed)
> > (gdb)
> > Continuing.
> > warning: .dynamic section for
> > "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2"
> > is not at the expected address (wrong library or version mismatch?)
> > warning: Unable to find libthread_db matching inferior's thread
> > library, thread debugging will not be available.
> > Stopped due to shared library event:
> >   Inferior loaded
> > /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so
> >     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
> >     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2
> > (gdb)
> > Continuing.
> > Stopped due to shared library event (no libraries added or removed)
> > (gdb)
> > Continuing.
> > Stopped due to shared library event:
> >   Inferior unloaded
> > /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so
> >     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6
> >     /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2
> > (gdb)
> > Continuing.
> > [Inferior 1 (process 638559) exited normally]
> > (gdb)
> >
> > (gdb) r --direct
> > Starting program:
> > /export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen4
> > --direct
> > warning: Unable to find libthread_db matching inferior's thread
> > library, thread debugging will not be available.
> > warning: .dynamic section for
> > "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2"
> > is not at the expected address (wrong library or version mismatch?)
> > warning: Unable to find libthread_db matching inferior's thread
> > library, thread debugging will not be available.
> > [Inferior 1 (process 638480) exited normally]
> > (gdb)
> >
> > It looks like l_ld of ld.so in the new namespace isn't set properly:
> >
> > (gdb) p debug->r_next->r_map
> > $5 = (struct link_map *) 0x408360
> > (gdb) p *debug->r_next->r_map
> > $6 = {l_addr = 140737351757824,
> >   l_name = 0x4082f0
> > "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/tst-dlmopen1mod.so",
> > l_ld = 0x7ffff7dbee10, l_next = 0x408900,
> >   l_prev = 0x0}
> > (gdb) p *debug->r_next->r_map->l_next
> > $7 = {l_addr = 140737349664768,
> >   l_name = 0x4088a0
> > "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/libc.so.6",
> > l_ld = 0x7ffff7daabc0, l_next = 0x408e90, l_prev = 0x408360}
> > (gdb) p *debug->r_next->r_map->l_next->l_next
> > $8 = {l_addr = 0,
> >   l_name = 0x408e20
> > "/export/build/gnu/tools-build/glibc-cet-gitlab/build-x86_64-linux/elf/ld-linux-x86-64.so.2",
> > l_ld = 0x0, l_next = 0x0, l_prev = 0x408900}
> > (gdb)
> >
> >
>
> Please try glibc users/hjl/pr15971/r_version branch:
>
> https://gitlab.com/x86-glibc/glibc/-/tree/users/hjl/pr15971/r_version
>
> and GDB users/hjl/pr11839/master branch:
>
> https://gitlab.com/x86-binutils/binutils-gdb/-/tree/users/hjl/pr11839/master
>

I updated glibc branch with minor improvements and fixed gdbserver
on GDB branch.  They work well together.
diff mbox series

Patch

diff --git a/csu/Makefile b/csu/Makefile
index 3054329cea..e2390e4a7d 100644
--- a/csu/Makefile
+++ b/csu/Makefile
@@ -88,6 +88,9 @@  endif
 before-compile += $(objpfx)abi-tag.h
 generated += abi-tag.h
 
+# Put it here to generate it earlier.
+gen-as-const-headers += rtld-sizes.sym
+
 # These are the special initializer/finalizer files.  They are always the
 # first and last file in the link.  crti.o ... crtn.o define the global
 # "functions" _init and _fini to run the .init and .fini sections.
diff --git a/csu/rtld-sizes.sym b/csu/rtld-sizes.sym
new file mode 100644
index 0000000000..40dd8edaec
--- /dev/null
+++ b/csu/rtld-sizes.sym
@@ -0,0 +1,4 @@ 
+#include <link.h>
+
+--
+COMPAT_R_DEBUG_SIZE	sizeof (struct r_debug)
diff --git a/elf/Makefile b/elf/Makefile
index 725537c40b..1444a53405 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -35,7 +35,8 @@  dl-routines	= $(addprefix dl-,load lookup object reloc deps \
 				  execstack open close trampoline \
 				  exception sort-maps lookup-direct \
 				  call-libc-early-init write \
-				  thread_gscope_wait tls_init_tp)
+				  thread_gscope_wait tls_init_tp \
+				  debug-symbols)
 ifeq (yes,$(use-ldconfig))
 dl-routines += dl-cache
 endif
@@ -203,7 +204,7 @@  tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \
 	 tst-align tst-align2 \
 	 tst-dlmodcount tst-dlopenrpath tst-deep1 \
-	 tst-dlmopen1 tst-dlmopen3 \
+	 tst-dlmopen1 tst-dlmopen3 tst-dlmopen4 \
 	 unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \
 	 tst-audit1 tst-audit2 tst-audit8 tst-audit9 \
 	 tst-addr1 tst-thrlock \
@@ -672,6 +673,21 @@  LC_ALL=C sed $(ldd-rewrite) < $< \
 endef
 endif
 
+ifeq ($(build-shared),yes)
+generated += dl-debug-compat-symbols.os dl-debug-compat-symbols.o
+
+libof-dl-debug-compat-symbols = rtld
+
+$(objpfx)dl-debug-compat-symbols.os: dl-debug-symbols-gen.c
+	$(compile-command.c) -S
+
+$(objpfx)dl-debug-compat-symbols.o: dl-debug-symbols-gen.c
+	$(compile-command.c) -S
+
+$(objpfx)dl-debug-symbols.os: $(objpfx)dl-debug-compat-symbols.os
+$(objpfx)dl-debug-symbols.o: $(objpfx)dl-debug-compat-symbols.o
+endif
+
 $(objpfx)ldd: ldd.bash.in $(common-objpfx)soversions.mk \
 	      $(common-objpfx)config.make
 	$(gen-ldd)
@@ -1242,6 +1258,8 @@  $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
 
 $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
 
+$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so
+
 $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
 tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
 
diff --git a/elf/dl-close.c b/elf/dl-close.c
index f39001cab9..f59ffdd666 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -500,7 +500,7 @@  _dl_close_worker (struct link_map *map, bool force)
 #endif
 
   /* Notify the debugger we are about to remove some loaded objects.  */
-  struct r_debug *r = _dl_debug_initialize (0, nsid);
+  struct r_debug_extended *r = _dl_debug_initialize (0, nsid);
   r->r_state = RT_DELETE;
   _dl_debug_state ();
   LIBC_PROBE (unmap_start, 2, nsid, r);
diff --git a/elf/dl-debug-symbols-gen.c b/elf/dl-debug-symbols-gen.c
new file mode 100644
index 0000000000..2406260bcb
--- /dev/null
+++ b/elf/dl-debug-symbols-gen.c
@@ -0,0 +1,24 @@ 
+/* Generate the _r_debug_extended symbol used to communicate dynamic
+   linker state to the debugger at runtime.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <link.h>
+
+/* This structure communicates dl state to the debugger.  The debugger
+   finds it via the DT_DEBUG entry in the dynamic section.  */
+struct r_debug_extended _r_debug_extended;
diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
new file mode 100644
index 0000000000..0966b172ab
--- /dev/null
+++ b/elf/dl-debug-symbols.S
@@ -0,0 +1,31 @@ 
+/* Define symbols used to communicate dynamic linker state to the
+   debugger at runtime.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <rtld-sizes.h>
+
+#ifdef SHARED
+# include "dl-debug-compat-symbols.os"
+#else
+# include "dl-debug-compat-symbols.o"
+#endif
+
+/* Define the compatibility symbol, _r_debug, with size of struct r_debug,
+   as an alias of _r_debug_extended.  */
+declare_object_symbol_alias (_r_debug, _r_debug_extended,
+			     COMPAT_R_DEBUG_SIZE);
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
index 2cd5f09753..9e884a5648 100644
--- a/elf/dl-debug.c
+++ b/elf/dl-debug.c
@@ -30,34 +30,35 @@  extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
 					  && VERIFY_MEMBER (l_prev))
 					 ? 1 : -1];
 
-/* This structure communicates dl state to the debugger.  The debugger
-   normally finds it via the DT_DEBUG entry in the dynamic section, but in
-   a statically-linked program there is no dynamic section for the debugger
-   to examine and it looks for this particular symbol name.  */
-struct r_debug _r_debug;
-
-
 /* Initialize _r_debug if it has not already been done.  The argument is
    the run-time load address of the dynamic linker, to be put in
    _r_debug.r_ldbase.  Returns the address of _r_debug.  */
 
-struct r_debug *
+struct r_debug_extended *
 _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
 {
-  struct r_debug *r;
+  struct r_debug_extended *r, *rp;
 
   if (ns == LM_ID_BASE)
-    r = &_r_debug;
+    r = &_r_debug_extended;
   else
-    r = &GL(dl_ns)[ns]._ns_debug;
+    {
+      r = &GL(dl_ns)[ns]._ns_debug;
+      rp = &GL(dl_ns)[ns - 1]._ns_debug;
+      rp->r_next = r;
+      if (ns - 1 == LM_ID_BASE)
+        _r_debug_extended.r_next = r;
+    }
 
   if (r->r_map == NULL || ldbase != 0)
     {
-      /* Tell the debugger where to find the map of loaded objects.  */
-      r->r_version = 1	/* R_DEBUG_VERSION XXX */;
-      r->r_ldbase = ldbase ?: _r_debug.r_ldbase;
+      /* Tell the debugger where to find the map of loaded objects.
+	 Bump r_version to 2 for the r_next field.  */
+      r->r_version = 2;
+      r->r_ldbase = ldbase ?: _r_debug_extended.r_ldbase;
       r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded;
       r->r_brk = (ElfW(Addr)) &_dl_debug_state;
+      r->r_next = NULL;
     }
 
   return r;
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 650e4edc35..cb0618b7fc 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -949,7 +949,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   /* Initialize to keep the compiler happy.  */
   const char *errstring = NULL;
   int errval = 0;
-  struct r_debug *r = _dl_debug_initialize (0, nsid);
+  struct r_debug_extended *r = _dl_debug_initialize (0, nsid);
   bool make_consistent = false;
 
   /* Get file information.  To match the kernel behavior, do not fill
diff --git a/elf/dl-open.c b/elf/dl-open.c
index ec386626f9..e90287bc62 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -630,7 +630,7 @@  dl_open_worker (void *a)
 #endif
 
   /* Notify the debugger all new objects are now ready to go.  */
-  struct r_debug *r = _dl_debug_initialize (0, args->nsid);
+  struct r_debug_extended *r = _dl_debug_initialize (0, args->nsid);
   r->r_state = RT_CONSISTENT;
   _dl_debug_state ();
   LIBC_PROBE (map_complete, 3, args->nsid, r, new);
diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
index d5bd2f31e9..b3016e1347 100644
--- a/elf/dl-reloc-static-pie.c
+++ b/elf/dl-reloc-static-pie.c
@@ -52,7 +52,7 @@  _dl_relocate_static_pie (void)
   main_map->l_relocated = 1;
 
   /* Initialize _r_debug.  */
-  struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
+  struct r_debug_extended *r = _dl_debug_initialize (0, LM_ID_BASE);
   r->r_state = RT_CONSISTENT;
 
   /* Set up debugging before the debugger is notified for the first
diff --git a/elf/link.h b/elf/link.h
index ff3a85c847..4c6391ff0b 100644
--- a/elf/link.h
+++ b/elf/link.h
@@ -34,14 +34,20 @@ 
 #include <bits/elfclass.h>		/* Defines __ELF_NATIVE_CLASS.  */
 #include <bits/link.h>
 
-/* Rendezvous structure used by the run-time dynamic linker to communicate
-   details of shared object loading to the debugger.  If the executable's
-   dynamic section has a DT_DEBUG element, the run-time linker sets that
-   element's value to the address where this structure can be found.  */
+enum r_debug_state
+  {
+    RT_CONSISTENT,	/* Mapping change is complete.  */
+    RT_ADD,		/* Beginning to add a new object.  */
+    RT_DELETE		/* Beginning to remove an object mapping.  */
+  };
+
+/* The legacy rendezvous structure used by the run-time dynamic linker to
+   communicate details of shared object loading to the debugger.  */
 
 struct r_debug
   {
-    int r_version;		/* Version number for this protocol.  */
+    /* Version number for this protocol.  It should be greater than 0.  */
+    int r_version;
 
     struct link_map *r_map;	/* Head of the chain of loaded objects.  */
 
@@ -51,28 +57,58 @@  struct r_debug
        The debugger can set a breakpoint at this address if it wants to
        notice shared object mapping changes.  */
     ElfW(Addr) r_brk;
-    enum
-      {
-	/* This state value describes the mapping change taking place when
-	   the `r_brk' address is called.  */
-	RT_CONSISTENT,		/* Mapping change is complete.  */
-	RT_ADD,			/* Beginning to add a new object.  */
-	RT_DELETE		/* Beginning to remove an object mapping.  */
-      } r_state;
+    /* This state value describes the mapping change taking place when
+       the `r_brk' address is called.  */
+    enum r_debug_state r_state;
 
     ElfW(Addr) r_ldbase;	/* Base address the linker is loaded at.  */
   };
 
-/* This is the instance of that structure used by the dynamic linker.  */
+/* This is the compatibility symbol of that structure provided by the
+   dynamic linker.  Access to its fields beyond r_ldbase may be invalid.
+ */
 extern struct r_debug _r_debug;
 
+/* The extended rendezvous structure used by the run-time dynamic linker
+   to communicate details of shared object loading to the debugger.  If
+   the executable's dynamic section has a DT_DEBUG element, the run-time
+   linker sets that element's value to the address where this structure
+   can be found.  */
+
+struct r_debug_extended
+  {
+    /* Version number for this protocol.  It should be greater than 0.  */
+    int r_version;
+
+    struct link_map *r_map;	/* Head of the chain of loaded objects.  */
+
+    /* This is the address of a function internal to the run-time linker,
+       that will always be called when the linker begins to map in a
+       library or unmap it, and again when the mapping change is complete.
+       The debugger can set a breakpoint at this address if it wants to
+       notice shared object mapping changes.  */
+    ElfW(Addr) r_brk;
+    /* This state value describes the mapping change taking place when
+       the `r_brk' address is called.  */
+    enum r_debug_state r_state;
+
+    ElfW(Addr) r_ldbase;	/* Base address the linker is loaded at.  */
+
+    /* The following field is added by r_version == 2.  */
+
+    /* Link to the next r_debug_extended structure.  Each r_debug_extended
+       structure represents a different namespace.  The first
+       r_debug_extended structure is for the default namespace.  */
+    struct r_debug_extended *r_next;
+  };
+
 /* This symbol refers to the "dynamic structure" in the `.dynamic' section
    of whatever module refers to `_DYNAMIC'.  So, to find its own
-   `struct r_debug', a program could do:
+   `struct r_debug_extended', a program could do:
      for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn)
        if (dyn->d_tag == DT_DEBUG)
-	 r_debug = (struct r_debug *) dyn->d_un.d_ptr;
-   */
+	 r_debug_extended = (struct r_debug_extended *) dyn->d_un.d_ptr;
+ */
 extern ElfW(Dyn) _DYNAMIC[];
 
 /* Structure describing a loaded shared object.  The `l_next' and `l_prev'
diff --git a/elf/rtld-debugger-interface.txt b/elf/rtld-debugger-interface.txt
index 61bc99e4b0..f6aaa28706 100644
--- a/elf/rtld-debugger-interface.txt
+++ b/elf/rtld-debugger-interface.txt
@@ -9,6 +9,9 @@  structure can be found.
 
 The r_debug structure contains (amongst others) the following fields:
 
+  int r_version:
+    Version number for this protocol.  It should be greater than 0.
+
   struct link_map *r_map:
     A linked list of loaded objects.
 
@@ -32,6 +35,17 @@  but there is no way for the debugger to discover whether any of the
 objects in the link-map have been relocated or not.
 
 
+Extension to the r_debug structure
+==================================
+
+The r_debug_extended structure is an extension of the r_debug interface.
+If r_version is 2, one additional field is available:
+
+  struct r_debug_extended *r_next;
+    Link to the next r_debug_extended structure.  Each r_debug_extended
+    structure represents a different namespace.  The first r_debug_extended
+    structure is for the default namespace.
+
 Probe-based debugger interface
 ==============================
 
diff --git a/elf/rtld.c b/elf/rtld.c
index 878e6480f4..6205dda3be 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1661,8 +1661,8 @@  dl_main (const ElfW(Phdr) *phdr,
   call_init_paths (&state);
 
   /* Initialize _r_debug.  */
-  struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr,
-					    LM_ID_BASE);
+  struct r_debug_extended *r
+    = _dl_debug_initialize (GL(dl_rtld_map).l_addr, LM_ID_BASE);
   r->r_state = RT_CONSISTENT;
 
   /* Put the link_map for ourselves on the chain so it can be found by
diff --git a/elf/tst-dlmopen4.c b/elf/tst-dlmopen4.c
new file mode 100644
index 0000000000..40d94aec31
--- /dev/null
+++ b/elf/tst-dlmopen4.c
@@ -0,0 +1,68 @@ 
+/* Test struct r_debug_extended via DT_DEBUG and DT_DEBUGSZ.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <link.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnu/lib-names.h>
+#include <support/xdlfcn.h>
+#include <support/check.h>
+#include <support/test-driver.h>
+
+#ifndef ELF_MACHINE_GET_R_DEBUG
+# define ELF_MACHINE_GET_R_DEBUG(d) \
+    (__extension__ ({ 						\
+      struct r_debug_extended *debug;				\
+      if ((d)->d_tag == DT_DEBUG)				\
+	debug = (struct r_debug_extended *) (d)->d_un.d_ptr;	\
+      else							\
+	debug = NULL;						\
+      debug; }))
+#endif
+
+static int
+do_test (void)
+{
+  void *h = xdlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so",
+		      RTLD_LAZY);
+
+  int status = EXIT_FAILURE;
+  ElfW(Dyn) *d;
+  for (d = _DYNAMIC; d->d_tag != DT_NULL; ++d)
+    {
+      struct r_debug_extended *debug = ELF_MACHINE_GET_R_DEBUG (d);
+      if (debug != NULL)
+	{
+	  TEST_VERIFY_EXIT (debug->r_version == 2);
+	  TEST_VERIFY_EXIT (debug->r_next != NULL);
+	  TEST_VERIFY_EXIT (debug->r_next->r_next == NULL);
+	  TEST_VERIFY_EXIT (debug->r_next->r_map != NULL);
+	  TEST_VERIFY_EXIT (debug->r_next->r_map->l_name != NULL);
+	  const char *name = basename (debug->r_next->r_map->l_name);
+	  TEST_VERIFY_EXIT (strcmp (name, "tst-dlmopen1mod.so") == 0);
+	  status = EXIT_SUCCESS;
+	}
+    }
+
+  xdlclose (h);
+
+  return status;
+}
+
+#include <support/test-driver.c>
diff --git a/include/link.h b/include/link.h
index 4af16cb596..7b8250db36 100644
--- a/include/link.h
+++ b/include/link.h
@@ -353,6 +353,10 @@  struct auditstate
 };
 
 
+/* This is the hidden instance of struct r_debug_extended used by the
+   dynamic linker.  */
+extern struct r_debug_extended _r_debug_extended attribute_hidden;
+
 #if __ELF_NATIVE_CLASS == 32
 # define symbind symbind32
 #elif __ELF_NATIVE_CLASS == 64
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index 9c15259236..a37b3fec82 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -355,7 +355,7 @@  struct rtld_global
       void (*free) (void *);
     } _ns_unique_sym_table;
     /* Keep track of changes to each namespace' list.  */
-    struct r_debug _ns_debug;
+    struct r_debug_extended _ns_debug;
   } _dl_ns[DL_NNS];
   /* One higher than index of last used namespace.  */
   EXTERN size_t _dl_nns;
@@ -1096,7 +1096,8 @@  rtld_hidden_proto (_dl_debug_state)
 /* Initialize `struct r_debug' if it has not already been done.  The
    argument is the run-time load address of the dynamic linker, to be put
    in the `r_ldbase' member.  Returns the address of the structure.  */
-extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
+extern struct r_debug_extended *_dl_debug_initialize (ElfW(Addr) ldbase,
+						      Lmid_t ns)
      attribute_hidden;
 
 /* Initialize the basic data structure for the search paths.  SOURCE